summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schanda <schanda@itestra.de>2013-09-17 13:58:27 +0200
committerPhilip Rauwolf <rauwolf@itestra.de>2013-09-18 18:43:20 +0200
commitcdddda28ea0c93c73dacafa17bf03f7f9bb4572a (patch)
treef0d590ec163f764f9d6128bbd4feef36869223c3
parentbee917e24eb806eb8ee7239fd563b9df6987d74b (diff)
downloadgenivi-common-api-dbus-runtime-cdddda28ea0c93c73dacafa17bf03f7f9bb4572a.tar.gz
Add managed services2.1.0
- Add proxy manager for controling client side of managed proxies - Add object manager for controling managed stubs - rework service registry and serviec publishing to use same code paths as managed objects - Unmanaged objects are now managed by the "/" object manager - Connection names are now only claimed after all object are activated - isAvailableBlocking public api on proxies - Proxies now require init to be called to allow for immediate callbacks on availability form the registry - Changed default commonp address transformation to use object path as base of instance - Fix issues with selective - Add pugixml for xml parsing to properly understand introspection ressponses - adds creating factory to objects held by proxy and stub adapter so that childeren can be safely constructed Change-Id: I8f2e660043d5e0348933513d39f332b7032288b1
-rw-r--r--.gitignore7
-rw-r--r--Makefile.am48
-rw-r--r--README2
-rw-r--r--src/CommonAPI/DBus/DBusAddressTranslator.cpp20
-rw-r--r--src/CommonAPI/DBus/DBusAddressTranslator.h9
-rw-r--r--src/CommonAPI/DBus/DBusConnection.cpp310
-rw-r--r--src/CommonAPI/DBus/DBusConnection.h20
-rw-r--r--src/CommonAPI/DBus/DBusDaemonProxy.cpp26
-rw-r--r--src/CommonAPI/DBus/DBusDaemonProxy.h45
-rw-r--r--src/CommonAPI/DBus/DBusFactory.cpp60
-rw-r--r--src/CommonAPI/DBus/DBusFactory.h29
-rw-r--r--src/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.h135
-rw-r--r--src/CommonAPI/DBus/DBusInterfaceHandler.h30
-rw-r--r--src/CommonAPI/DBus/DBusObjectManager.cpp129
-rw-r--r--src/CommonAPI/DBus/DBusObjectManager.h37
-rw-r--r--src/CommonAPI/DBus/DBusObjectManagerStub.cpp283
-rw-r--r--src/CommonAPI/DBus/DBusObjectManagerStub.h149
-rw-r--r--src/CommonAPI/DBus/DBusProxy.cpp14
-rw-r--r--src/CommonAPI/DBus/DBusProxy.h11
-rw-r--r--src/CommonAPI/DBus/DBusProxyBase.h3
-rw-r--r--src/CommonAPI/DBus/DBusProxyConnection.h6
-rw-r--r--src/CommonAPI/DBus/DBusProxyHelper.h351
-rw-r--r--src/CommonAPI/DBus/DBusProxyManager.cpp151
-rw-r--r--src/CommonAPI/DBus/DBusProxyManager.h71
-rw-r--r--src/CommonAPI/DBus/DBusServicePublisher.cpp129
-rw-r--r--src/CommonAPI/DBus/DBusServicePublisher.h24
-rw-r--r--src/CommonAPI/DBus/DBusServiceRegistry.cpp1367
-rw-r--r--src/CommonAPI/DBus/DBusServiceRegistry.h260
-rw-r--r--src/CommonAPI/DBus/DBusServiceStatusEvent.cpp53
-rw-r--r--src/CommonAPI/DBus/DBusServiceStatusEvent.h47
-rw-r--r--src/CommonAPI/DBus/DBusStubAdapter.cpp28
-rw-r--r--src/CommonAPI/DBus/DBusStubAdapter.h42
-rw-r--r--src/CommonAPI/DBus/DBusStubAdapterHelper.h8
-rw-r--r--src/pugixml/pugiconfig.hpp69
-rw-r--r--src/pugixml/pugixml.cpp10250
-rw-r--r--src/pugixml/pugixml.hpp1265
-rw-r--r--src/test/DBusConnectionTest.cpp29
-rw-r--r--src/test/DBusManagedTest.cpp351
-rw-r--r--src/test/DBusObjectManagerStubTest.cpp472
-rw-r--r--src/test/DBusProxyTest.cpp88
-rw-r--r--src/test/DBusSelectiveBroadcastTest.cpp5
-rw-r--r--src/test/DBusServiceRegistryTest.cpp467
-rw-r--r--src/test/DBusTestUtils.h14
-rw-r--r--src/test/commonapi/tests/DerivedTypeCollection.cpp20
-rw-r--r--src/test/commonapi/tests/DerivedTypeCollection.h56
-rw-r--r--src/test/commonapi/tests/PredefinedTypeCollection.h23
-rw-r--r--src/test/commonapi/tests/TestInterface.h10
-rw-r--r--src/test/commonapi/tests/TestInterfaceDBusProxy.cpp31
-rw-r--r--src/test/commonapi/tests/TestInterfaceDBusProxy.h21
-rw-r--r--src/test/commonapi/tests/TestInterfaceDBusStubAdapter.cpp86
-rw-r--r--src/test/commonapi/tests/TestInterfaceDBusStubAdapter.h28
-rw-r--r--src/test/commonapi/tests/TestInterfaceProxy.h80
-rw-r--r--src/test/commonapi/tests/TestInterfaceProxyBase.h10
-rw-r--r--src/test/commonapi/tests/TestInterfaceStub.h53
-rw-r--r--src/test/commonapi/tests/TestInterfaceStubDefault.cpp120
-rw-r--r--src/test/commonapi/tests/TestInterfaceStubDefault.h31
-rw-r--r--src/test/commonapi/tests/managed/BranchInterface.h90
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.cpp74
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.h61
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.cpp89
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.h62
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceProxy.h174
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceProxyBase.h47
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceStub.h91
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceStubDefault.cpp43
-rw-r--r--src/test/commonapi/tests/managed/BranchInterfaceStubDefault.h63
-rw-r--r--src/test/commonapi/tests/managed/LeafInterface.h90
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.cpp74
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.h61
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.cpp89
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.h62
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceProxy.h174
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceProxyBase.h47
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceStub.h91
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceStubDefault.cpp43
-rw-r--r--src/test/commonapi/tests/managed/LeafInterfaceStubDefault.h63
-rw-r--r--src/test/commonapi/tests/managed/RootInterface.h91
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceDBusProxy.cpp82
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceDBusProxy.h66
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.cpp203
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.h70
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceProxy.h184
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceProxyBase.h52
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceStub.h105
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceStubDefault.cpp76
-rw-r--r--src/test/commonapi/tests/managed/RootInterfaceStubDefault.h72
-rw-r--r--src/test/fake/legacy/service/LegacyInterface.h10
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceDBusProxy.cpp22
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceDBusProxy.h18
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.cpp105
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.h62
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceProxy.h59
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceProxyBase.h106
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceStub.h95
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceStubDefault.cpp59
-rw-r--r--src/test/fake/legacy/service/LegacyInterfaceStubDefault.h69
-rw-r--r--src/test/fakeLegacyService.fidl (renamed from src/test/fakeLegacyService/fakeLegacyService.fidl)0
-rw-r--r--src/test/leaf.fidl26
-rw-r--r--src/test/root.fidl51
99 files changed, 19339 insertions, 1415 deletions
diff --git a/.gitignore b/.gitignore
index bd5facb..388251b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,12 @@
.deps
.dirstamp
.libs
+*.gz
*.la
*.lo
+*.log
*.o
-*.gz
+*.trs
/.autotools
/.cproject
/.project
@@ -38,6 +40,7 @@
/DBusAddressTranslatorTest
/.pydevproject
/DBusServiceRegistryTest
+/DBusClientIdTest
/DBusConnectionTest
/DBusMainLoopIntegrationTest
/DBusServicePublisherTest
@@ -47,5 +50,7 @@
/DBusDynamicLoadingBasicTest
/DBusDynamicLoadingMultipleBindingsTest
/DBusDynamicLoadingNoValidityTest
+/DBusObjectManagerStubTest
/DBusClientIdTest
/DBusSelectiveBroadcastTest
+/DBusManagedTest
diff --git a/Makefile.am b/Makefile.am
index d20ed01..c61c246 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,6 +34,13 @@ libmurmurhash_internal_la_SOURCES = \
src/murmurhash/MurmurHash3.cpp
# ------------------------------------------------------------------------------
+noinst_LTLIBRARIES += libpugixml-internal.la
+
+libpugixml_internal_la_SOURCES = \
+ src/pugixml/pugiconfig.hpp \
+ src/pugixml/pugixml.cpp \
+ src/pugixml/pugixml.hpp
+# ------------------------------------------------------------------------------
lib_LTLIBRARIES += libCommonAPI-DBus.la
libCommonAPI_DBus_la_SOURCES = \
@@ -48,23 +55,30 @@ libCommonAPI_DBus_la_SOURCES = \
src/CommonAPI/DBus/DBusMainLoopContext.cpp \
src/CommonAPI/DBus/DBusMessage.cpp \
src/CommonAPI/DBus/DBusObjectManager.cpp \
+ src/CommonAPI/DBus/DBusObjectManagerStub.cpp \
src/CommonAPI/DBus/DBusOutputStream.cpp \
src/CommonAPI/DBus/DBusProxyBase.cpp \
src/CommonAPI/DBus/DBusProxy.cpp \
+ src/CommonAPI/DBus/DBusProxyManager.cpp \
src/CommonAPI/DBus/DBusFactory.cpp \
src/CommonAPI/DBus/DBusRuntime.cpp \
src/CommonAPI/DBus/DBusServicePublisher.cpp \
src/CommonAPI/DBus/DBusServiceRegistry.cpp \
- src/CommonAPI/DBus/DBusServiceStatusEvent.cpp \
src/CommonAPI/DBus/DBusStubAdapter.cpp
libCommonAPI_DBus_la_LIBADD = \
libmurmurhash-internal.la \
+ libpugixml-internal.la \
${COMMONAPI_LIBS}
libCommonAPI_DBus_la_LDFLAGS = \
${AM_LDFLAGS} \
-version-info ${LIBCOMMONAPI_DBUS_CURRENT}:${LIBCOMMONAPI_DBUS_REVISION}:${LIBCOMMONAPI_DBUS_AGE}
+CommonAPI_DBus_pugi_includedir=$(includedir)/CommonAPI-${VERSION}/pugixml
+CommonAPI_DBus_pugi_include_HEADERS = \
+ src/pugixml/pugixml.hpp \
+ src/pugixml/pugiconfig.hpp
+
CommonAPI_DBus_includedir=$(includedir)/CommonAPI-${VERSION}/CommonAPI/DBus
CommonAPI_DBus_include_HEADERS = \
src/CommonAPI/DBus/DBusAddressTranslator.h \
@@ -79,15 +93,19 @@ CommonAPI_DBus_include_HEADERS = \
src/CommonAPI/DBus/DBusFunctionalHash.h \
src/CommonAPI/DBus/DBusHelper.h \
src/CommonAPI/DBus/DBusInputStream.h \
+ src/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.h \
+ src/CommonAPI/DBus/DBusInterfaceHandler.h \
src/CommonAPI/DBus/DBusMainLoopContext.h \
src/CommonAPI/DBus/DBusMessage.h \
src/CommonAPI/DBus/DBusMultiEvent.h \
src/CommonAPI/DBus/DBusObjectManager.h \
+ src/CommonAPI/DBus/DBusObjectManagerStub.h \
src/CommonAPI/DBus/DBusOutputStream.h \
src/CommonAPI/DBus/DBusProxyBase.h \
src/CommonAPI/DBus/DBusProxy.h \
src/CommonAPI/DBus/DBusProxyAsyncCallbackHandler.h \
src/CommonAPI/DBus/DBusProxyConnection.h \
+ src/CommonAPI/DBus/DBusProxyManager.h \
src/CommonAPI/DBus/DBusFactory.h \
src/CommonAPI/DBus/DBusProxyHelper.h \
src/CommonAPI/DBus/DBusRuntime.h \
@@ -95,7 +113,6 @@ CommonAPI_DBus_include_HEADERS = \
src/CommonAPI/DBus/DBusSerializableArguments.h \
src/CommonAPI/DBus/DBusServicePublisher.h \
src/CommonAPI/DBus/DBusServiceRegistry.h \
- src/CommonAPI/DBus/DBusServiceStatusEvent.h \
src/CommonAPI/DBus/DBusStubAdapter.h \
src/CommonAPI/DBus/DBusStubAdapterHelper.h \
src/CommonAPI/DBus/DBusUtils.h \
@@ -132,6 +149,18 @@ TestInterfaceDBusSources = \
src/test/commonapi/tests/TestInterfaceDBusStubAdapter.cpp \
src/test/fake/legacy/service/LegacyInterfaceDBusProxy.cpp
+ManagedSources = \
+ src/test/commonapi/tests/managed/LeafInterfaceStubDefault.cpp \
+ src/test/commonapi/tests/managed/RootInterfaceStubDefault.cpp
+
+ManagedDBusSources = \
+ ${ManagedSources} \
+ src/test/commonapi/tests/managed/RootInterfaceDBusProxy.cpp \
+ src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.cpp \
+ src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.cpp \
+ src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.cpp
+
+
lib_LTLIBRARIES += libDBusGen-TestInterface.la
libDBusGen_TestInterface_la_SOURCES = \
${TestInterfaceDBusSources}
@@ -157,6 +186,7 @@ DYNAMIC_LOADING_TEST_SET = \
DBusDynamicLoadingMultipleBindingsTest
check_PROGRAMS = \
+ DBusManagedTest \
DBusRuntimeTest \
${DYNAMIC_LOADING_TEST_SET} \
DBusMainLoopIntegrationTest \
@@ -175,13 +205,21 @@ check_PROGRAMS = \
DBusMultipleConnectionTest \
DBusServicePublisherTest \
DBusClientIdTest \
+ DBusObjectManagerStubTest \
DBusSelectiveBroadcastTest
-
+noinst_PROGRAMS = ${check_PROGRAMS}
TESTS = ${check_PROGRAMS}
+
LDADD_FOR_GTEST_WITHOUT_LIBCOMMON_API_DBUS = ${GTEST_LIBS} ${LDADD}
LDADD_FOR_GTEST = libCommonAPI-DBus.la ${LDADD_FOR_GTEST_WITHOUT_LIBCOMMON_API_DBUS}
+DBusManagedTest_SOURCES = \
+ src/test/DBusManagedTest.cpp \
+ ${ManagedDBusSources}
+DBusManagedTest_CPPFLAGS = ${AM_CPPFLAGS} ${GTEST_CPPFLAGS} ${GLIB_CFLAGS}
+DBusManagedTest_CXXFLAGS = ${GTEST_CXXFLAGS} ${GLIB_CFLAGS}
+DBusManagedTest_LDADD = ${LDADD_FOR_GTEST} ${GLIB_LIBS}
DBusMainLoopIntegrationTest_SOURCES = \
src/test/DBusMainLoopIntegrationTest.cpp \
@@ -324,6 +362,10 @@ DBusClientIdTest_CPPFLAGS = ${AM_CPPFLAGS} ${GTEST_CPPFLAGS}
DBusClientIdTest_CXXFLAGS = ${GTEST_CXXFLAGS}
DBusClientIdTest_LDADD = ${LDADD_FOR_GTEST}
+DBusObjectManagerStubTest_SOURCES = src/test/DBusObjectManagerStubTest.cpp
+DBusObjectManagerStubTest_CPPFLAGS = ${AM_CPPFLAGS} ${GTEST_CPPFLAGS}
+DBusObjectManagerStubTest_CXXFLAGS = ${GTEST_CXXFLAGS}
+DBusObjectManagerStubTest_LDADD = ${LDADD_FOR_GTEST}
DBusSelectiveBroadcastTest_SOURCES = \
${TestInterfaceDBusSources} \
diff --git a/README b/README
index 66e39be..fb64e17 100644
--- a/README
+++ b/README
@@ -144,6 +144,8 @@ per binary or globally for all binaries. If more than one config file is defined
across several config files, the locally defined values override the global ones. If a CommonAPI address is defined more than once
within a single file, the first definition found will be the only definition used.
+Note: Only one managing interface can be defined per D-Bus object path!
+
CommonAPI D-Bus Config files have to be named this way:
----
# Binary local: "<FqnOfBinary>_dbus.conf", e.g. "/usr/bin/myBinary_dbus.conf" if the binary is "/usr/bin/myBinary"
diff --git a/src/CommonAPI/DBus/DBusAddressTranslator.cpp b/src/CommonAPI/DBus/DBusAddressTranslator.cpp
index 6b6afce..5a81199 100644
--- a/src/CommonAPI/DBus/DBusAddressTranslator.cpp
+++ b/src/CommonAPI/DBus/DBusAddressTranslator.cpp
@@ -21,6 +21,7 @@
#include <iostream>
#include <fstream>
#include <cassert>
+#include <sstream>
namespace CommonAPI {
@@ -48,6 +49,17 @@ DBusAddressTranslator& DBusAddressTranslator::getInstance() {
}
+void DBusAddressTranslator::searchForDBusAddress(const std::string& domain,
+ const std::string& interface,
+ const std::string& instance,
+ std::string& interfaceName,
+ std::string& connectionName,
+ std::string& objectPath) {
+ std::stringstream ss;
+ ss << domain << ":" << interface << ":" << instance;
+ searchForDBusAddress(ss.str(), interfaceName, connectionName, objectPath);
+}
+
void DBusAddressTranslator::searchForDBusAddress(const std::string& commonApiAddress,
std::string& interfaceName,
std::string& connectionName,
@@ -124,7 +136,13 @@ void DBusAddressTranslator::findFallbackCommonAddress(std::string& commonApiAddr
const std::string& interfaceName,
const std::string& connectionName,
const std::string& objectPath) const {
- commonApiAddress = "local:" + interfaceName + ":" + connectionName;
+ commonApiAddress = "local:" + interfaceName + ":" + transfromObjectPathToInstance(objectPath);
+}
+
+std::string DBusAddressTranslator::transfromObjectPathToInstance(const std::string& path) const {
+ std::string out = path.substr(1, std::string::npos);
+ std::replace(out.begin(), out.end(), '/', '.');
+ return out;
}
}// namespace DBus
diff --git a/src/CommonAPI/DBus/DBusAddressTranslator.h b/src/CommonAPI/DBus/DBusAddressTranslator.h
index 89cebf1..f55b6df 100644
--- a/src/CommonAPI/DBus/DBusAddressTranslator.h
+++ b/src/CommonAPI/DBus/DBusAddressTranslator.h
@@ -32,6 +32,13 @@ public:
static DBusAddressTranslator& getInstance();
+ void searchForDBusAddress(const std::string& domain,
+ const std::string& interface,
+ const std::string& instance,
+ std::string& interfaceName,
+ std::string& connectionName,
+ std::string& objectPath);
+
void searchForDBusAddress(const std::string& commonApiAddress,
std::string& interfaceName,
std::string& connectionName,
@@ -64,6 +71,8 @@ private:
void fillUndefinedValues(CommonApiServiceDetails& serviceDetails, const std::string& commonApiAddress) const;
+ std::string transfromObjectPathToInstance(const std::string& path) const;
+
std::unordered_map<std::string, CommonApiServiceDetails> commonApiAddressDetails_;
std::unordered_map<DBusServiceAddress, std::string> dbusToCommonApiAddress_;
};
diff --git a/src/CommonAPI/DBus/DBusConnection.cpp b/src/CommonAPI/DBus/DBusConnection.cpp
index c867136..1a88c60 100644
--- a/src/CommonAPI/DBus/DBusConnection.cpp
+++ b/src/CommonAPI/DBus/DBusConnection.cpp
@@ -89,7 +89,9 @@ DBusConnection::DBusConnection(BusType busType) :
connectionNameCount_(),
dispatchSource_(),
mainLoopContext_(std::shared_ptr<MainLoopContext>(NULL)),
- enforcerThread_(NULL) {
+ enforcerThread_(NULL),
+ libdbusSignalMatchRulesCount_(0) {
+
dbus_threads_init_default();
}
@@ -105,7 +107,8 @@ DBusConnection::DBusConnection(::DBusConnection* libDbusConnection) :
connectionNameCount_(),
dispatchSource_(),
mainLoopContext_(std::shared_ptr<MainLoopContext>(NULL)),
- enforcerThread_(NULL) {
+ enforcerThread_(NULL),
+ libdbusSignalMatchRulesCount_(0) {
dbus_threads_init_default();
}
@@ -324,8 +327,9 @@ void DBusConnection::disconnect() {
if (isConnected()) {
dbusConnectionStatusEvent_.notifyListeners(AvailabilityStatus::NOT_AVAILABLE);
- if (!dbusSignalMatchRulesMap_.empty()) {
+ if (libdbusSignalMatchRulesCount_ > 0) {
dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
+ libdbusSignalMatchRulesCount_ = 0;
}
connectionNameCount_.clear();
@@ -374,6 +378,7 @@ const std::shared_ptr<DBusServiceRegistry> DBusConnection::getDBusServiceRegistr
return serviceRegistry;
}
+//Does this need to be a weak pointer?
const std::shared_ptr<DBusObjectManager> DBusConnection::getDBusObjectManager() {
if (!dbusObjectManager_) {
objectManagerGuard_.lock();
@@ -506,10 +511,13 @@ void DBusConnection::enforceAsynchronousTimeouts() const {
if (!dbus_pending_call_get_completed(libdbusPendingCall)) {
dbus_pending_call_cancel(libdbusPendingCall);
- DBusMessageReplyAsyncHandler* asyncHandler = std::get<1>(it->second);
- DBusMessage& dbusMessageCall = std::get<2>(it->second);
- asyncHandler->onDBusMessageReply(CallStatus::REMOTE_ERROR, dbusMessageCall.createMethodError(DBUS_ERROR_TIMEOUT));
- delete asyncHandler;
+ DBusMessageReplyAsyncHandler* asyncHandler = std::get<1>(it->second);
+ DBusMessage& dbusMessageCall = std::get<2>(it->second);
+ enforceTimeoutMutex_.unlock(); // unlock before making callbacks to application to avoid deadlocks
+ asyncHandler->onDBusMessageReply(CallStatus::REMOTE_ERROR, dbusMessageCall.createMethodError(DBUS_ERROR_TIMEOUT));
+ enforceTimeoutMutex_.lock();
+ delete asyncHandler;
+
}
it = timeoutMap_.erase(it);
@@ -718,6 +726,170 @@ bool DBusConnection::removeSignalMemberHandler(const DBusSignalHandlerToken& dbu
return lastHandlerRemoved;
}
+bool DBusConnection::addObjectManagerSignalMemberHandler(const std::string& dbusBusName,
+ DBusSignalHandler* dbusSignalHandler) {
+ if (dbusBusName.length() < 2) {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> dbusSignalLock(dbusObjectManagerSignalGuard_);
+ auto dbusSignalMatchRuleIterator = dbusObjectManagerSignalMatchRulesMap_.find(dbusBusName);
+ const bool isDBusSignalMatchRuleFound = (dbusSignalMatchRuleIterator != dbusObjectManagerSignalMatchRulesMap_.end());
+
+ if (!isDBusSignalMatchRuleFound) {
+ if (isConnected() && !addObjectManagerSignalMatchRule(dbusBusName)) {
+ return false;
+ }
+
+ auto insertResult = dbusObjectManagerSignalMatchRulesMap_.insert({ dbusBusName, 0 });
+ const bool isInsertSuccessful = insertResult.second;
+
+ if (!isInsertSuccessful) {
+ if (isConnected()) {
+ const bool isRemoveSignalMatchRuleSuccessful = removeObjectManagerSignalMatchRule(dbusBusName);
+ assert(isRemoveSignalMatchRuleSuccessful);
+ }
+
+ return false;
+ }
+
+ dbusSignalMatchRuleIterator = insertResult.first;
+ }
+
+ size_t& dbusSignalMatchRuleRefernceCount = dbusSignalMatchRuleIterator->second;
+ dbusSignalMatchRuleRefernceCount++;
+
+ dbusObjectManagerSignalHandlerTable_.insert({ dbusBusName, dbusSignalHandler });
+
+ return true;
+}
+
+bool DBusConnection::removeObjectManagerSignalMemberHandler(const std::string& dbusBusName,
+ DBusSignalHandler* dbusSignalHandler) {
+ assert(!dbusBusName.empty());
+
+ std::lock_guard<std::mutex> dbusSignalLock(dbusObjectManagerSignalGuard_);
+ auto dbusSignalMatchRuleIterator = dbusObjectManagerSignalMatchRulesMap_.find(dbusBusName);
+ const bool isDBusSignalMatchRuleFound = (dbusSignalMatchRuleIterator != dbusObjectManagerSignalMatchRulesMap_.end());
+
+ if (!isDBusSignalMatchRuleFound) {
+ return true;
+ }
+
+ auto dbusObjectManagerSignalHandlerRange = dbusObjectManagerSignalHandlerTable_.equal_range(dbusBusName);
+ auto dbusObjectManagerSignalHandlerIterator = std::find_if(
+ dbusObjectManagerSignalHandlerRange.first,
+ dbusObjectManagerSignalHandlerRange.second,
+ [&](decltype(*dbusObjectManagerSignalHandlerRange.first)& it) { return it.second == dbusSignalHandler; });
+ const bool isDBusSignalHandlerFound = (dbusObjectManagerSignalHandlerIterator != dbusObjectManagerSignalHandlerRange.second);
+
+ if (!isDBusSignalHandlerFound) {
+ return false;
+ }
+
+ size_t& dbusSignalMatchRuleReferenceCount = dbusSignalMatchRuleIterator->second;
+
+ assert(dbusSignalMatchRuleReferenceCount > 0);
+ dbusSignalMatchRuleReferenceCount--;
+
+ const bool isLastDBusSignalMatchRuleReference = (dbusSignalMatchRuleReferenceCount == 0);
+ if (isLastDBusSignalMatchRuleReference) {
+ if (isConnected() && !removeObjectManagerSignalMatchRule(dbusBusName)) {
+ return false;
+ }
+
+ dbusObjectManagerSignalMatchRulesMap_.erase(dbusSignalMatchRuleIterator);
+ }
+
+ dbusObjectManagerSignalHandlerTable_.erase(dbusObjectManagerSignalHandlerIterator);
+
+ return true;
+}
+
+bool DBusConnection::addObjectManagerSignalMatchRule(const std::string& dbusBusName) {
+ std::ostringstream dbusMatchRuleStringStream;
+
+ dbusMatchRuleStringStream << "type='signal'"
+ << ",sender='" << dbusBusName << "'"
+ << ",interface='org.freedesktop.DBus.ObjectManager'";
+
+ return addLibdbusSignalMatchRule(dbusMatchRuleStringStream.str());
+}
+
+bool DBusConnection::removeObjectManagerSignalMatchRule(const std::string& dbusBusName) {
+ std::ostringstream dbusMatchRuleStringStream;
+
+ dbusMatchRuleStringStream << "type='signal'"
+ << ",sender='" << dbusBusName << "'"
+ << ",interface='org.freedesktop.DBus.ObjectManager'";
+
+ return removeLibdbusSignalMatchRule(dbusMatchRuleStringStream.str());
+}
+
+/**
+ * Called only if connected
+ *
+ * @param dbusMatchRule
+ * @return
+ */
+bool DBusConnection::addLibdbusSignalMatchRule(const std::string& dbusMatchRule) {
+ bool libdbusSuccess = true;
+
+ suspendDispatching();
+
+ // add the libdbus message signal filter
+ if (!libdbusSignalMatchRulesCount_) {
+ libdbusSuccess = (bool) dbus_connection_add_filter(
+ libdbusConnection_,
+ &onLibdbusSignalFilterThunk,
+ this,
+ NULL);
+ }
+
+ // finally add the match rule
+ if (libdbusSuccess) {
+ DBusError dbusError;
+ dbus_bus_add_match(libdbusConnection_, dbusMatchRule.c_str(), &dbusError.libdbusError_);
+ libdbusSuccess = !dbusError;
+ }
+
+ if (libdbusSuccess) {
+ libdbusSignalMatchRulesCount_++;
+ }
+
+ resumeDispatching();
+
+ return libdbusSuccess;
+}
+
+/**
+ * Called only if connected
+ *
+ * @param dbusMatchRule
+ * @return
+ */
+bool DBusConnection::removeLibdbusSignalMatchRule(const std::string& dbusMatchRule) {
+ //assert(libdbusSignalMatchRulesCount_ > 0);
+ if(libdbusSignalMatchRulesCount_ == 0)
+ return true;
+
+ suspendDispatching();
+
+ DBusError dbusError;
+ dbus_bus_remove_match(libdbusConnection_, dbusMatchRule.c_str(), &dbusError.libdbusError_);
+
+ if (!dbusError) {
+ libdbusSignalMatchRulesCount_--;
+ if (libdbusSignalMatchRulesCount_ == 0) {
+ dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
+ }
+ }
+
+ resumeDispatching();
+
+ return true;
+}
+
void DBusConnection::registerObjectPath(const std::string& objectPath) {
assert(!objectPath.empty());
assert(objectPath[0] == '/');
@@ -801,7 +973,6 @@ void DBusConnection::addLibdbusSignalMatchRule(const std::string& objectPath,
DBusSignalMatchRuleMapping(1, matchRuleString)));
assert(success.second);
- // if not connected the filter and the rules will be added as soon as the connection is established
if (isConnected()) {
suspendDispatching();
// add the libdbus message signal filter
@@ -841,23 +1012,13 @@ void DBusConnection::removeLibdbusSignalMatchRule(const std::string& objectPath,
return;
}
- const std::string& matchRuleString = matchRuleIterator->second.second;
-
- suspendDispatching();
-
- DBusError dbusError;
- dbus_bus_remove_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
-
- assert(!dbusError);
-
- dbusSignalMatchRulesMap_.erase(matchRuleIterator);
-
- const bool isLastMatchRule = dbusSignalMatchRulesMap_.empty();
- if (isLastMatchRule) {
- dbus_connection_remove_filter(libdbusConnection_, &onLibdbusSignalFilterThunk, this);
+ if (isConnected()) {
+ const std::string& matchRuleString = matchRuleIterator->second.second;
+ const bool libdbusSuccess = removeLibdbusSignalMatchRule(matchRuleString);
+ assert(libdbusSuccess);
}
- resumeDispatching();
+ dbusSignalMatchRulesMap_.erase(matchRuleIterator);
}
void DBusConnection::initLibdbusObjectPathHandlerAfterConnect() {
@@ -891,29 +1052,20 @@ void DBusConnection::initLibdbusObjectPathHandlerAfterConnect() {
void DBusConnection::initLibdbusSignalFilterAfterConnect() {
assert(isConnected());
- // nothing to do if there aren't any signal match rules
- if (dbusSignalMatchRulesMap_.empty())
- return;
-
- suspendDispatching();
-
- // first we add the libdbus message signal filter
- const dbus_bool_t libdbusSuccess = dbus_connection_add_filter(libdbusConnection_,
- &onLibdbusSignalFilterThunk,
- this,
- NULL);
- assert(libdbusSuccess);
-
- // then we upload all match rules to the dbus-daemon
- DBusError dbusError;
- for (auto iterator = dbusSignalMatchRulesMap_.begin(); iterator != dbusSignalMatchRulesMap_.end(); iterator++) {
- const std::string& matchRuleString = iterator->second.second;
+ // proxy/stub match rules
+ for (const auto& dbusSignalMatchRuleIterator : dbusSignalMatchRulesMap_) {
+ const auto& dbusSignalMatchRuleMapping = dbusSignalMatchRuleIterator.second;
+ const std::string& dbusMatchRuleString = dbusSignalMatchRuleMapping.second;
+ const bool libdbusSuccess = addLibdbusSignalMatchRule(dbusMatchRuleString);
+ assert(libdbusSuccess);
+ }
- dbusError.clear();
- dbus_bus_add_match(libdbusConnection_, matchRuleString.c_str(), &dbusError.libdbusError_);
- assert(!dbusError);
+ // object manager match rules (see DBusServiceRegistry)
+ for (const auto& dbusObjectManagerSignalMatchRuleIterator : dbusObjectManagerSignalMatchRulesMap_) {
+ const std::string& dbusBusName = dbusObjectManagerSignalMatchRuleIterator.first;
+ const bool libdbusSuccess = addObjectManagerSignalMatchRule(dbusBusName);
+ assert(libdbusSuccess);
}
- resumeDispatching();
}
::DBusHandlerResult DBusConnection::onLibdbusObjectPathMessage(::DBusMessage* libdbusMessage) {
@@ -928,6 +1080,29 @@ void DBusConnection::initLibdbusSignalFilterAfterConnect() {
return isDBusMessageHandled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+template<typename DBusSignalHandlersTable>
+void notifyDBusSignalHandlers(DBusSignalHandlersTable& dbusSignalHandlerstable,
+ std::pair<typename DBusSignalHandlersTable::iterator,
+ typename DBusSignalHandlersTable::iterator>& equalRange,
+ const CommonAPI::DBus::DBusMessage& dbusMessage,
+ ::DBusHandlerResult& dbusHandlerResult) {
+ if (equalRange.first != equalRange.second) {
+ dbusHandlerResult = DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ while (equalRange.first != equalRange.second) {
+ DBusProxyConnection::DBusSignalHandler* dbusSignalHandler = equalRange.first->second;
+
+ auto dbusSignalHandlerSubscriptionStatus = dbusSignalHandler->onSignalDBusMessage(dbusMessage);
+
+ if (dbusSignalHandlerSubscriptionStatus == SubscriptionStatus::CANCEL) {
+ equalRange.first = dbusSignalHandlerstable.erase(equalRange.first);
+ } else {
+ equalRange.first++;
+ }
+ }
+}
+
::DBusHandlerResult DBusConnection::onLibdbusSignalFilter(::DBusMessage* libdbusMessage) {
assert(libdbusMessage);
@@ -948,28 +1123,35 @@ void DBusConnection::initLibdbusSignalFilterAfterConnect() {
assert(interfaceMemberName);
assert(interfaceMemberSignature);
- std::lock_guard<std::mutex> dbusSignalLock(signalGuard_);
- DBusSignalHandlerPath dbusSignalHandlerPath(objectPath, interfaceName, interfaceMemberName, interfaceMemberSignature);
- auto equalRangeIteratorPair = dbusSignalHandlerTable_.equal_range(dbusSignalHandlerPath);
-
- if (equalRangeIteratorPair.first != equalRangeIteratorPair.second) {
- DBusMessage dbusMessage(libdbusMessage);
-
- while (equalRangeIteratorPair.first != equalRangeIteratorPair.second) {
- DBusSignalHandler* dbusSignalHandler = equalRangeIteratorPair.first->second;
- const SubscriptionStatus dbusSignalHandlerSubscriptionStatus = dbusSignalHandler->onSignalDBusMessage(dbusMessage);
-
- if (dbusSignalHandlerSubscriptionStatus == SubscriptionStatus::CANCEL) {
- equalRangeIteratorPair.first = dbusSignalHandlerTable_.erase(equalRangeIteratorPair.first);
- } else {
- equalRangeIteratorPair.first++;
- }
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
+ DBusMessage dbusMessage(libdbusMessage);
+ ::DBusHandlerResult dbusHandlerResult = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ signalGuard_.lock();
+ auto dbusSignalHandlerIteratorPair = dbusSignalHandlerTable_.equal_range(DBusSignalHandlerPath(
+ objectPath,
+ interfaceName,
+ interfaceMemberName,
+ interfaceMemberSignature));
+ notifyDBusSignalHandlers(dbusSignalHandlerTable_,
+ dbusSignalHandlerIteratorPair,
+ dbusMessage,
+ dbusHandlerResult);
+ signalGuard_.unlock();
+
+ if (dbusMessage.hasInterfaceName("org.freedesktop.DBus.ObjectManager")) {
+ const char* dbusSenderName = dbusMessage.getSenderName();
+ assert(dbusSenderName);
+
+ dbusObjectManagerSignalGuard_.lock();
+ auto dbusObjectManagerSignalHandlerIteratorPair = dbusObjectManagerSignalHandlerTable_.equal_range(dbusSenderName);
+ notifyDBusSignalHandlers(dbusObjectManagerSignalHandlerTable_,
+ dbusObjectManagerSignalHandlerIteratorPair,
+ dbusMessage,
+ dbusHandlerResult);
+ dbusObjectManagerSignalGuard_.unlock();
}
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ return dbusHandlerResult;
}
::DBusHandlerResult DBusConnection::onLibdbusSignalFilterThunk(::DBusConnection* libdbusConnection,
diff --git a/src/CommonAPI/DBus/DBusConnection.h b/src/CommonAPI/DBus/DBusConnection.h
index 9ee5588..07e30f7 100644
--- a/src/CommonAPI/DBus/DBusConnection.h
+++ b/src/CommonAPI/DBus/DBusConnection.h
@@ -21,6 +21,9 @@
#include <dbus/dbus.h>
+#include <atomic>
+
+
namespace CommonAPI {
namespace DBus {
@@ -89,6 +92,11 @@ class DBusConnection: public DBusProxyConnection, public std::enable_shared_from
DBusError& dbusError,
int timeoutMilliseconds = kDefaultSendTimeoutMs) const;
+ virtual bool addObjectManagerSignalMemberHandler(const std::string& dbusBusName,
+ DBusSignalHandler* dbusSignalHandler);
+ virtual bool removeObjectManagerSignalMemberHandler(const std::string& dbusBusName,
+ DBusSignalHandler* dbusSignalHandler);
+
DBusSignalHandlerToken addSignalMemberHandler(const std::string& objectPath,
const std::string& interfaceName,
const std::string& interfaceMemberName,
@@ -200,6 +208,18 @@ class DBusConnection: public DBusProxyConnection, public std::enable_shared_from
DBusSignalHandlerTable dbusSignalHandlerTable_;
+ std::unordered_map<std::string, size_t> dbusObjectManagerSignalMatchRulesMap_;
+ std::unordered_multimap<std::string, DBusSignalHandler*> dbusObjectManagerSignalHandlerTable_;
+ std::mutex dbusObjectManagerSignalGuard_;
+
+ bool addObjectManagerSignalMatchRule(const std::string& dbusBusName);
+ bool removeObjectManagerSignalMatchRule(const std::string& dbusBusName);
+
+ bool addLibdbusSignalMatchRule(const std::string& dbusMatchRule);
+ bool removeLibdbusSignalMatchRule(const std::string& dbusMatchRule);
+
+ std::atomic_size_t libdbusSignalMatchRulesCount_;
+
// objectPath, referenceCount
typedef std::unordered_map<std::string, uint32_t> LibdbusRegisteredObjectPathHandlersTable;
LibdbusRegisteredObjectPathHandlersTable libdbusRegisteredObjectPaths_;
diff --git a/src/CommonAPI/DBus/DBusDaemonProxy.cpp b/src/CommonAPI/DBus/DBusDaemonProxy.cpp
index 1c804ee..24d4d16 100644
--- a/src/CommonAPI/DBus/DBusDaemonProxy.cpp
+++ b/src/CommonAPI/DBus/DBusDaemonProxy.cpp
@@ -42,6 +42,10 @@ DBusDaemonProxy::DBusDaemonProxy(const std::shared_ptr<DBusProxyConnection>& dbu
nameOwnerChangedEvent_(*this, "NameOwnerChanged", "sss") {
}
+void DBusDaemonProxy::init() {
+
+}
+
std::string DBusDaemonProxy::getAddress() const {
return getDomain() + ":" + getServiceId() + ":" + getInstanceId();
}
@@ -69,6 +73,10 @@ bool DBusDaemonProxy::isAvailable() const {
return getDBusConnection()->isConnected();
}
+bool DBusDaemonProxy::isAvailableBlocking() const {
+ return isAvailable();
+}
+
ProxyStatusEvent& DBusDaemonProxy::getProxyStatusEvent() {
return getDBusConnection()->getConnectionStatusEvent();
}
@@ -173,6 +181,24 @@ std::future<CallStatus> DBusDaemonProxy::getManagedObjectsAsync(const std::strin
2000);
}
+std::future<CallStatus> DBusDaemonProxy::getNameOwnerAsync(const std::string& busName, GetNameOwnerAsyncCallback getNameOwnerAsyncCallback) const {
+ DBusMessage dbusMessage = createMethodCall("GetNameOwner", "s");
+
+ DBusOutputStream outputStream(dbusMessage);
+ const bool success = DBusSerializableArguments<std::string>::serialize(outputStream, busName);
+ if (!success) {
+ std::promise<CallStatus> promise;
+ promise.set_value(CallStatus::OUT_OF_MEMORY);
+ return promise.get_future();
+ }
+ outputStream.flush();
+
+ return getDBusConnection()->sendDBusMessageWithReplyAsync(
+ dbusMessage,
+ DBusProxyAsyncCallbackHandler<std::string>::create(getNameOwnerAsyncCallback),
+ 2000);
+}
+
} // namespace DBus
} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusDaemonProxy.h b/src/CommonAPI/DBus/DBusDaemonProxy.h
index b785f29..14f7799 100644
--- a/src/CommonAPI/DBus/DBusDaemonProxy.h
+++ b/src/CommonAPI/DBus/DBusDaemonProxy.h
@@ -37,24 +37,27 @@ class StaticInterfaceVersionAttribute: public InterfaceVersionAttribute {
class DBusDaemonProxy: public DBusProxyBase {
public:
- typedef Event<std::string, std::string, std::string> NameOwnerChangedEvent;
+ typedef Event<std::string, std::string, std::string> NameOwnerChangedEvent;
- typedef std::unordered_map<std::string, int> PropertyDictStub;
- typedef std::unordered_map<std::string, PropertyDictStub> InterfaceToPropertyDict;
- typedef std::unordered_map<std::string, InterfaceToPropertyDict> DBusObjectToInterfaceDict;
+ typedef std::unordered_map<std::string, int> PropertyDictStub;
+ typedef std::unordered_map<std::string, PropertyDictStub> InterfaceToPropertyDict;
+ typedef std::unordered_map<std::string, InterfaceToPropertyDict> DBusObjectToInterfaceDict;
- typedef std::function<void(const CommonAPI::CallStatus&, std::vector<std::string>)> ListNamesAsyncCallback;
- typedef std::function<void(const CommonAPI::CallStatus&, bool)> NameHasOwnerAsyncCallback;
- typedef std::function<void(const CommonAPI::CallStatus&, DBusObjectToInterfaceDict)> GetManagedObjectsAsyncCallback;
+ typedef std::function<void(const CommonAPI::CallStatus&, std::vector<std::string>)> ListNamesAsyncCallback;
+ typedef std::function<void(const CommonAPI::CallStatus&, bool)> NameHasOwnerAsyncCallback;
+ typedef std::function<void(const CommonAPI::CallStatus&, DBusObjectToInterfaceDict)> GetManagedObjectsAsyncCallback;
+ typedef std::function<void(const CommonAPI::CallStatus&, std::string)> GetNameOwnerAsyncCallback;
+ DBusDaemonProxy(const std::shared_ptr<DBusProxyConnection>& dbusConnection);
- DBusDaemonProxy(const std::shared_ptr<DBusProxyConnection>& dbusConnection);
+ virtual bool isAvailable() const;
+ virtual bool isAvailableBlocking() const;
+ virtual ProxyStatusEvent& getProxyStatusEvent();
+ virtual InterfaceVersionAttribute& getInterfaceVersionAttribute();
- virtual bool isAvailable() const;
- virtual ProxyStatusEvent& getProxyStatusEvent();
- virtual InterfaceVersionAttribute& getInterfaceVersionAttribute();
+ void init();
- static inline const char* getInterfaceId();
+ static inline const char* getInterfaceId();
NameOwnerChangedEvent& getNameOwnerChangedEvent();
@@ -62,9 +65,21 @@ class DBusDaemonProxy: public DBusProxyBase {
std::future<CallStatus> listNamesAsync(ListNamesAsyncCallback listNamesAsyncCallback) const;
void nameHasOwner(const std::string& busName, CommonAPI::CallStatus& callStatus, bool& hasOwner) const;
- std::future<CallStatus> nameHasOwnerAsync(const std::string& busName, NameHasOwnerAsyncCallback nameHasOwnerAsyncCallback) const;
-
- std::future<CallStatus> getManagedObjectsAsync(const std::string& forDBusServiceName, GetManagedObjectsAsyncCallback) const;
+ std::future<CallStatus> nameHasOwnerAsync(const std::string& busName,
+ NameHasOwnerAsyncCallback nameHasOwnerAsyncCallback) const;
+
+ std::future<CallStatus> getManagedObjectsAsync(const std::string& forDBusServiceName,
+ GetManagedObjectsAsyncCallback) const;
+
+ /**
+ * Get the unique connection/bus name of the primary owner of the name given
+ *
+ * @param busName Name to get the owner of
+ * @param getNameOwnerAsyncCallback callback functor
+ *
+ * @return CallStatus::REMOTE_ERROR if the name is unknown, otherwise CallStatus::SUCCESS and the uniq name of the owner
+ */
+ std::future<CallStatus> getNameOwnerAsync(const std::string& busName, GetNameOwnerAsyncCallback getNameOwnerAsyncCallback) const;
virtual std::string getAddress() const;
virtual const std::string& getDomain() const;
diff --git a/src/CommonAPI/DBus/DBusFactory.cpp b/src/CommonAPI/DBus/DBusFactory.cpp
index 572f8f7..4571a4a 100644
--- a/src/CommonAPI/DBus/DBusFactory.cpp
+++ b/src/CommonAPI/DBus/DBusFactory.cpp
@@ -94,7 +94,7 @@ bool DBusFactory::isServiceInstanceAlive(const std::string& participantId,
}
-SubscriptionStatus DBusFactory::isServiceInstanceAliveCallbackThunk(Factory::IsServiceInstanceAliveCallback callback, const AvailabilityStatus& status) {
+SubscriptionStatus DBusFactory::isServiceInstanceAliveCallbackThunk(Factory::IsServiceInstanceAliveCallback callback, const AvailabilityStatus& status, std::shared_ptr<DBusServiceRegistry> serviceRegistry) {
callback(status == AvailabilityStatus::AVAILABLE);
return SubscriptionStatus::CANCEL;
}
@@ -106,16 +106,26 @@ void DBusFactory::isServiceInstanceAliveAsync(Factory::IsServiceInstanceAliveCal
DBusAddressTranslator::getInstance().searchForDBusAddress(serviceAddress, interfaceName, connectionName, objectPath);
- dbusConnection_->getDBusServiceRegistry()->subscribeAvailabilityListener(
+ std::shared_ptr<DBusServiceRegistry> serviceRegistry = dbusConnection_->getDBusServiceRegistry();
+
+ serviceRegistry->subscribeAvailabilityListener(
serviceAddress,
std::bind(&DBusFactory::isServiceInstanceAliveCallbackThunk,
this,
callback,
- std::placeholders::_1)
+ std::placeholders::_1,
+ serviceRegistry)
);
}
-void DBusFactory::isServiceInstanceAliveAsync(Factory::IsServiceInstanceAliveCallback callback, const std::string& serviceInstanceID, const std::string& serviceName, const std::string& serviceDomainName) {
+std::shared_ptr<CommonAPI::DBus::DBusConnection> DBusFactory::getDbusConnection() {
+ return dbusConnection_;
+}
+
+void DBusFactory::isServiceInstanceAliveAsync(Factory::IsServiceInstanceAliveCallback callback,
+ const std::string& serviceInstanceID,
+ const std::string& serviceName,
+ const std::string& serviceDomainName) {
std::string commonApiAddress = serviceDomainName + ":" + serviceName + ":" + serviceInstanceID;
isServiceInstanceAliveAsync(callback, commonApiAddress);
}
@@ -139,18 +149,20 @@ std::shared_ptr<Proxy> DBusFactory::createProxy(const char* interfaceId,
for (auto it = registeredProxyFactoryFunctions_->begin(); it != registeredProxyFactoryFunctions_->end(); ++it) {
if(it->first == interfaceId) {
- return (it->second)(commonApiAddress, interfaceName, connectionName, objectPath, dbusConnection_);
+ std::shared_ptr<DBusProxy> proxy = (it->second)(shared_from_this(), commonApiAddress, interfaceName, connectionName, objectPath, dbusConnection_);
+ proxy->init();
+ return proxy;
}
}
return NULL;
}
-bool DBusFactory::registerAdapter(std::shared_ptr<StubBase> stubBase,
- const char* interfaceId,
- const std::string& participantId,
- const std::string& serviceName,
- const std::string& domain) {
+std::shared_ptr<DBusStubAdapter> DBusFactory::createDBusStubAdapter(const std::shared_ptr<StubBase>& stubBase,
+ const char* interfaceId,
+ const std::string& participantId,
+ const std::string& serviceName,
+ const std::string& domain) {
assert(dbusConnection_->isConnected());
std::string commonApiAddress = domain + ":" + serviceName + ":" + participantId;
@@ -161,28 +173,22 @@ bool DBusFactory::registerAdapter(std::shared_ptr<StubBase> stubBase,
DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, interfaceName, connectionName, objectPath);
- bool isServiceNameAcquired = dbusConnection_->requestServiceNameAndBlock(connectionName);
- if (!isServiceNameAcquired) {
- return false;
- }
-
- if(!registeredAdapterFactoryFunctions_) {
+ if (!registeredAdapterFactoryFunctions_) {
registeredAdapterFactoryFunctions_ = new std::unordered_map<std::string, DBusAdapterFactoryFunction> {};
}
- auto foundFunction = registeredAdapterFactoryFunctions_->find(interfaceId);
- if(foundFunction != registeredAdapterFactoryFunctions_->end()) {
- std::shared_ptr<DBusStubAdapter> dbusStubAdapter = (foundFunction->second)(commonApiAddress, interfaceName, connectionName, objectPath, dbusConnection_, stubBase);
- if(!dbusStubAdapter) {
- return false;
- }
- if(DBusServicePublisher::getInstance()->registerService(commonApiAddress, dbusStubAdapter)) {
- dbusStubAdapter->init();
- return true;
- }
+ auto registeredAdapterFactoryFunctionsIter = registeredAdapterFactoryFunctions_->find(interfaceId);
+ const bool hasRegisteredAdapterFactoryFunctions = (registeredAdapterFactoryFunctionsIter != registeredAdapterFactoryFunctions_->end());
+ std::shared_ptr<DBusStubAdapter> dbusStubAdapter;
+
+ if (hasRegisteredAdapterFactoryFunctions) {
+ const auto& dbusAdapterFactoryFunction = registeredAdapterFactoryFunctionsIter->second;
+
+ dbusStubAdapter = dbusAdapterFactoryFunction(shared_from_this(), commonApiAddress, interfaceName, connectionName, objectPath, dbusConnection_, stubBase);
+ dbusStubAdapter->init();
}
- return false;
+ return dbusStubAdapter;
}
bool DBusFactory::unregisterService(const std::string& participantId, const std::string& serviceName, const std::string& domain) {
diff --git a/src/CommonAPI/DBus/DBusFactory.h b/src/CommonAPI/DBus/DBusFactory.h
index f9132f1..1c4ddbf 100644
--- a/src/CommonAPI/DBus/DBusFactory.h
+++ b/src/CommonAPI/DBus/DBusFactory.h
@@ -24,21 +24,24 @@ namespace CommonAPI {
namespace DBus {
class DBusMainLoopContext;
+class DBusFactory;
-typedef std::shared_ptr<DBusProxy> (*DBusProxyFactoryFunction) (const std::string& commonApiAddress,
- const std::string& interfaceName,
- const std::string& busName,
- const std::string& objectPath,
- const std::shared_ptr<DBusProxyConnection>& dbusProxyConnection);
+typedef std::shared_ptr<DBusProxy> (*DBusProxyFactoryFunction)(const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<DBusProxyConnection>& dbusProxyConnection);
-typedef std::shared_ptr<DBusStubAdapter> (*DBusAdapterFactoryFunction) (const std::string& commonApiAddress,
+typedef std::shared_ptr<DBusStubAdapter> (*DBusAdapterFactoryFunction) (const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
const std::string& objectPath,
const std::shared_ptr<DBusProxyConnection>& dbusProxyConnection,
const std::shared_ptr<StubBase>& stubBase);
-class DBusFactory: public Factory {
+class DBusFactory: public Factory, public std::enable_shared_from_this<DBusFactory> {
public:
DBusFactory(std::shared_ptr<Runtime> runtime, const MiddlewareInfo* middlewareInfo, std::shared_ptr<MainLoopContext> mainLoopContext, const DBusFactoryConfig& dbusFactoryConfig = DBusFactoryConfig());
@@ -58,12 +61,18 @@ class DBusFactory: public Factory {
virtual bool unregisterService(const std::string& participantId, const std::string& serviceName, const std::string& domain = "local");
- protected:
+ std::shared_ptr<DBusStubAdapter> createDBusStubAdapter(const std::shared_ptr<StubBase>& stubBase,
+ const char* interfaceId,
+ const std::string& participantId,
+ const std::string& serviceName,
+ const std::string& domain);
+
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> getDbusConnection();
+
virtual std::shared_ptr<Proxy> createProxy(const char* interfaceId, const std::string& participantId, const std::string& serviceName, const std::string& domain);
- virtual bool registerAdapter(std::shared_ptr<StubBase> stubBase, const char* interfaceId, const std::string& participantId, const std::string& serviceName, const std::string& domain);
private:
- SubscriptionStatus isServiceInstanceAliveCallbackThunk(Factory::IsServiceInstanceAliveCallback callback, const AvailabilityStatus& status);
+ SubscriptionStatus isServiceInstanceAliveCallbackThunk(Factory::IsServiceInstanceAliveCallback callback, const AvailabilityStatus& status, std::shared_ptr<DBusServiceRegistry> serviceRegistry);
std::shared_ptr<CommonAPI::DBus::DBusConnection> dbusConnection_;
std::shared_ptr<MainLoopContext> mainLoopContext_;
diff --git a/src/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.h b/src/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.h
new file mode 100644
index 0000000..d9bb975
--- /dev/null
+++ b/src/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#error "Only <CommonAPI/CommonAPI.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef COMMONAPI_DBUS_DBUS_INSTANCE_AVAILABILITY_STATUS_CHANGED_EVENT_H_
+#define COMMONAPI_DBUS_DBUS_INSTANCE_AVAILABILITY_STATUS_CHANGED_EVENT_H_
+
+#include <CommonAPI/ProxyManager.h>
+
+#include "DBusProxy.h"
+#include "DBusObjectManagerStub.h"
+#include "DBusInstanceAvailabilityStatusChangedEvent.h"
+
+#include <functional>
+#include <future>
+#include <string>
+#include <vector>
+
+
+namespace CommonAPI {
+namespace DBus {
+
+// TODO move logic to DBusServiceRegistry, now every proxy will deserialize the messages!
+class DBusInstanceAvailabilityStatusChangedEvent:
+ public ProxyManager::InstanceAvailabilityStatusChangedEvent,
+ public DBusProxyConnection::DBusSignalHandler {
+ public:
+ DBusInstanceAvailabilityStatusChangedEvent(DBusProxy& dbusProxy, const char* interfaceName) :
+ dbusProxy_(dbusProxy),
+ observedInterfaceName_(interfaceName) {
+ }
+
+ virtual SubscriptionStatus onSignalDBusMessage(const DBusMessage& dbusMessage) {
+ if (dbusMessage.hasMemberName("InterfacesAdded")) {
+ onInterfacesAddedSignal(dbusMessage);
+ } else if (dbusMessage.hasMemberName("InterfacesRemoved")) {
+ onInterfacesRemovedSignal(dbusMessage);
+ }
+
+ return CommonAPI::SubscriptionStatus::RETAIN;
+ }
+
+ protected:
+ virtual void onFirstListenerAdded(const CancellableListener&) {
+ interfacesAddedSubscription_ = dbusProxy_.addSignalMemberHandler(
+ dbusProxy_.getDBusObjectPath(),
+ DBusObjectManagerStub::getInterfaceName(),
+ "InterfacesAdded",
+ "oa{sa{sv}}",
+ this);
+
+ interfacesRemovedSubscription_ = dbusProxy_.addSignalMemberHandler(
+ dbusProxy_.getDBusObjectPath(),
+ DBusObjectManagerStub::getInterfaceName(),
+ "InterfacesRemoved",
+ "oas",
+ this);
+ }
+
+ virtual void onLastListenerRemoved(const CancellableListener&) {
+ dbusProxy_.removeSignalMemberHandler(interfacesAddedSubscription_);
+ dbusProxy_.removeSignalMemberHandler(interfacesRemovedSubscription_);
+ }
+
+ private:
+ inline void onInterfacesAddedSignal(const DBusMessage& dbusMessage) {
+ DBusInputStream dbusInputStream(dbusMessage);
+ std::string dbusObjectPath;
+ DBusObjectManagerStub::DBusInterfacesAndPropertiesDict dbusInterfacesAndPropertiesDict;
+
+ dbusInputStream >> dbusObjectPath;
+ assert(!dbusInputStream.hasError());
+
+ dbusInputStream >> dbusInterfacesAndPropertiesDict;
+ assert(!dbusInputStream.hasError());
+
+ for (const auto& dbusInterfaceIterator : dbusInterfacesAndPropertiesDict) {
+ const std::string& dbusInterfaceName = dbusInterfaceIterator.first;
+
+ if(dbusInterfaceName == observedInterfaceName_) {
+ notifyInterfaceStatusChanged(dbusObjectPath, dbusInterfaceName, AvailabilityStatus::AVAILABLE);
+ }
+ }
+ }
+
+ inline void onInterfacesRemovedSignal(const DBusMessage& dbusMessage) {
+ DBusInputStream dbusInputStream(dbusMessage);
+ std::string dbusObjectPath;
+ std::vector<std::string> dbusInterfaceNames;
+
+ dbusInputStream >> dbusObjectPath;
+ assert(!dbusInputStream.hasError());
+
+ dbusInputStream >> dbusInterfaceNames;
+ assert(!dbusInputStream.hasError());
+
+ for (const auto& dbusInterfaceName : dbusInterfaceNames) {
+ if(dbusInterfaceName == observedInterfaceName_) {
+ notifyInterfaceStatusChanged(dbusObjectPath, dbusInterfaceName, AvailabilityStatus::NOT_AVAILABLE);
+ }
+ }
+ }
+
+ void notifyInterfaceStatusChanged(const std::string& dbusObjectPath,
+ const std::string& dbusInterfaceName,
+ const AvailabilityStatus& availabilityStatus) {
+ std::string commonApiAddress;
+
+ DBusAddressTranslator::getInstance().searchForCommonAddress(
+ dbusInterfaceName,
+ dbusProxy_.getDBusBusName(),
+ dbusObjectPath,
+ commonApiAddress);
+
+ notifyListeners(commonApiAddress, availabilityStatus);
+ }
+
+
+ DBusProxy& dbusProxy_;
+ std::string observedInterfaceName_;
+ DBusProxyConnection::DBusSignalHandlerToken interfacesAddedSubscription_;
+ DBusProxyConnection::DBusSignalHandlerToken interfacesRemovedSubscription_;
+};
+
+} // namespace DBus
+} // namespace CommonAPI
+
+#endif // COMMONAPI_DBUS_DBUS_INSTANCE_AVAILABILITY_STATUS_CHANGED_EVENT_H_
diff --git a/src/CommonAPI/DBus/DBusInterfaceHandler.h b/src/CommonAPI/DBus/DBusInterfaceHandler.h
new file mode 100644
index 0000000..92b65fa
--- /dev/null
+++ b/src/CommonAPI/DBus/DBusInterfaceHandler.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef COMMONAPI_DBUS_INTERFACE_HANDLER_H_
+#define COMMONAPI_DBUS_INTERFACE_HANDLER_H_
+
+#include "DBusProxyConnection.h"
+#include "DBusMessage.h"
+
+#include <memory>
+
+namespace CommonAPI {
+namespace DBus {
+
+class DBusInterfaceHandler {
+ public:
+ virtual ~DBusInterfaceHandler() { }
+
+ virtual const char* getMethodsDBusIntrospectionXmlData() const = 0;
+
+ virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage) = 0;
+};
+
+} // namespace dbus
+} // namespace CommonAPI
+
+#endif // COMMONAPI_DBUS_INTERFACE_HANDLER_H_
diff --git a/src/CommonAPI/DBus/DBusObjectManager.cpp b/src/CommonAPI/DBus/DBusObjectManager.cpp
index 96c61d1..69a4f2a 100644
--- a/src/CommonAPI/DBus/DBusObjectManager.cpp
+++ b/src/CommonAPI/DBus/DBusObjectManager.cpp
@@ -23,13 +23,18 @@ namespace CommonAPI {
namespace DBus {
DBusObjectManager::DBusObjectManager(const std::shared_ptr<DBusProxyConnection>& dbusConnection):
- dbusConnection_(dbusConnection) {
+ dbusConnection_(dbusConnection),
+ rootDBusObjectManagerStub_("/", dbusConnection) {
if (!dbusConnection->isObjectPathMessageHandlerSet()) {
dbusConnection->setObjectPathMessageHandler(
std::bind(&DBusObjectManager::handleMessage, this, std::placeholders::_1));
}
dbusConnection->registerObjectPath("/");
+
+ dbusRegisteredObjectsTable_.insert({
+ DBusInterfaceHandlerPath("/", DBusObjectManagerStub::getInterfaceName()),
+ &rootDBusObjectManagerStub_ });
}
DBusObjectManager::~DBusObjectManager() {
@@ -40,38 +45,71 @@ DBusObjectManager::~DBusObjectManager() {
}
}
-DBusInterfaceHandlerToken DBusObjectManager::registerDBusStubAdapter(const std::string& objectPath,
- const std::string& interfaceName,
- DBusStubAdapter* dbusStubAdapter) {
- DBusInterfaceHandlerPath handlerPath(objectPath, interfaceName);
+bool DBusObjectManager::registerDBusStubAdapter(DBusStubAdapter* dbusStubAdapter) {
+ const auto& dbusStubAdapterObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusStubAdapterInterfaceName = dbusStubAdapter->getInterfaceName();
+ DBusInterfaceHandlerPath dbusStubAdapterHandlerPath(dbusStubAdapterObjectPath, dbusStubAdapterInterfaceName);
+ bool isRegistrationSuccessful = false;
objectPathLock_.lock();
- bool noSuchHandlerRegistered = dbusRegisteredObjectsTable_.find(handlerPath) == dbusRegisteredObjectsTable_.end();
+ isRegistrationSuccessful = addDBusInterfaceHandler(dbusStubAdapterHandlerPath, dbusStubAdapter);
- assert(noSuchHandlerRegistered);
+ if (isRegistrationSuccessful && dbusStubAdapter->hasDBusObjectManagerStub()) {
+ auto dbusObjectManagerStub = dbusStubAdapter->getDBusObjectManagerStub();
+ assert(dbusObjectManagerStub);
- dbusRegisteredObjectsTable_.insert({handlerPath, dbusStubAdapter});
- objectPathLock_.unlock();
+ isRegistrationSuccessful = addDBusInterfaceHandler(
+ { dbusStubAdapterObjectPath, dbusObjectManagerStub->getInterfaceName() }, dbusObjectManagerStub);
- std::shared_ptr<DBusProxyConnection> dbusConnection = dbusConnection_.lock();
- if (dbusConnection) {
- dbusConnection->registerObjectPath(objectPath);
+ if (!isRegistrationSuccessful) {
+ const bool isDBusStubAdapterRemoved = removeDBusInterfaceHandler(dbusStubAdapterHandlerPath, dbusStubAdapter);
+ assert(isDBusStubAdapterRemoved);
+ }
}
- return handlerPath;
+ if (isRegistrationSuccessful) {
+ std::shared_ptr<DBusProxyConnection> dbusConnection = dbusConnection_.lock();
+ if (dbusConnection) {
+ dbusConnection->registerObjectPath(dbusStubAdapterObjectPath);
+ }
+ }
+ objectPathLock_.unlock();
+
+ return isRegistrationSuccessful;
}
-void DBusObjectManager::unregisterDBusStubAdapter(const DBusInterfaceHandlerToken& dbusInterfaceHandlerToken) {
+bool DBusObjectManager::unregisterDBusStubAdapter(DBusStubAdapter* dbusStubAdapter) {
+ const auto& dbusStubAdapterObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusStubAdapterInterfaceName = dbusStubAdapter->getInterfaceName();
+ DBusInterfaceHandlerPath dbusStubAdapterHandlerPath(dbusStubAdapterObjectPath, dbusStubAdapterInterfaceName);
+ bool isDeregistrationSuccessful = false;
+
objectPathLock_.lock();
- const std::string& objectPath = dbusInterfaceHandlerToken.first;
+ isDeregistrationSuccessful = removeDBusInterfaceHandler(dbusStubAdapterHandlerPath, dbusStubAdapter);
+
+ if (isDeregistrationSuccessful && dbusStubAdapter->hasDBusObjectManagerStub()) {
+ auto dbusObjectManagerStub = dbusStubAdapter->getDBusObjectManagerStub();
+ assert(dbusObjectManagerStub);
+
+ isDeregistrationSuccessful = removeDBusInterfaceHandler(
+ { dbusStubAdapterObjectPath, dbusObjectManagerStub->getInterfaceName() }, dbusObjectManagerStub);
- std::shared_ptr<DBusProxyConnection> lockedConnection = dbusConnection_.lock();
- if (lockedConnection) {
- lockedConnection->unregisterObjectPath(objectPath);
+ if (!isDeregistrationSuccessful) {
+ const bool isDBusStubAdapterAdded = addDBusInterfaceHandler(dbusStubAdapterHandlerPath, dbusStubAdapter);
+ assert(isDBusStubAdapterAdded);
+ }
+ }
+
+ if (isDeregistrationSuccessful) {
+ std::shared_ptr<DBusProxyConnection> lockedConnection = dbusConnection_.lock();
+ if (lockedConnection) {
+ lockedConnection->unregisterObjectPath(dbusStubAdapterObjectPath);
+ }
}
- dbusRegisteredObjectsTable_.erase(dbusInterfaceHandlerToken);
objectPathLock_.unlock();
+
+ return isDeregistrationSuccessful;
}
bool DBusObjectManager::handleMessage(const DBusMessage& dbusMessage) {
@@ -89,53 +127,44 @@ bool DBusObjectManager::handleMessage(const DBusMessage& dbusMessage) {
bool dbusMessageHandled = false;
if (foundDBusInterfaceHandler) {
- DBusStubAdapter* dbusStubAdapter = handlerIterator->second;
- dbusMessageHandled = dbusStubAdapter->onInterfaceDBusMessage(dbusMessage);
+ DBusInterfaceHandler* dbusStubAdapterBase = handlerIterator->second;
+ dbusMessageHandled = dbusStubAdapterBase->onInterfaceDBusMessage(dbusMessage);
} else if (dbusMessage.hasInterfaceName("org.freedesktop.DBus.Introspectable")) {
dbusMessageHandled = onIntrospectableInterfaceDBusMessage(dbusMessage);
- } else if (dbusMessage.hasInterfaceName("org.freedesktop.DBus.ObjectManager")) {
- dbusMessageHandled = onObjectManagerInterfaceDBusMessage(dbusMessage);
}
objectPathLock_.unlock();
return dbusMessageHandled;
}
-bool DBusObjectManager::onObjectManagerInterfaceDBusMessage(const DBusMessage& dbusMessage) {
- std::shared_ptr<DBusProxyConnection> dbusConnection = dbusConnection_.lock();
+bool DBusObjectManager::addDBusInterfaceHandler(const DBusInterfaceHandlerPath& dbusInterfaceHandlerPath,
+ DBusInterfaceHandler* dbusInterfaceHandler) {
+ const auto& dbusRegisteredObjectsTableIter = dbusRegisteredObjectsTable_.find(dbusInterfaceHandlerPath);
+ const bool isDBusInterfaceHandlerAlreadyAdded = (dbusRegisteredObjectsTableIter != dbusRegisteredObjectsTable_.end());
- if (!dbusConnection || !dbusMessage.isMethodCallType() || !dbusMessage.hasMemberName("GetManagedObjects")) {
+ if (isDBusInterfaceHandlerAlreadyAdded) {
return false;
}
- DBusDaemonProxy::DBusObjectToInterfaceDict resultObjectPathsInterfacesAndPropertiesDict;
+ auto insertResult = dbusRegisteredObjectsTable_.insert({ dbusInterfaceHandlerPath, dbusInterfaceHandler });
+ const bool insertSuccess = insertResult.second;
- objectPathLock_.lock();
- auto registeredObjectsIterator = dbusRegisteredObjectsTable_.begin();
+ return insertSuccess;
+}
- while(registeredObjectsIterator != dbusRegisteredObjectsTable_.end()) {
- DBusInterfaceHandlerPath handlerPath = registeredObjectsIterator->first;
- auto foundDictEntry = resultObjectPathsInterfacesAndPropertiesDict.find(handlerPath.first);
+bool DBusObjectManager::removeDBusInterfaceHandler(const DBusInterfaceHandlerPath& dbusInterfaceHandlerPath,
+ DBusInterfaceHandler* dbusInterfaceHandler) {
+ const auto& dbusRegisteredObjectsTableIter = dbusRegisteredObjectsTable_.find(dbusInterfaceHandlerPath);
+ const bool isDBusInterfaceHandlerAdded = (dbusRegisteredObjectsTableIter != dbusRegisteredObjectsTable_.end());
- if (foundDictEntry == resultObjectPathsInterfacesAndPropertiesDict.end()) {
- resultObjectPathsInterfacesAndPropertiesDict.insert(
- { handlerPath.first, { { handlerPath.second, DBusDaemonProxy::PropertyDictStub() } } } );
- } else {
- foundDictEntry->second.insert( {handlerPath.second, DBusDaemonProxy::PropertyDictStub() } );
- }
+ if (isDBusInterfaceHandlerAdded) {
+ auto registeredDBusStubAdapter = dbusRegisteredObjectsTableIter->second;
+ assert(registeredDBusStubAdapter == dbusInterfaceHandler);
- ++registeredObjectsIterator;
+ dbusRegisteredObjectsTable_.erase(dbusRegisteredObjectsTableIter);
}
- objectPathLock_.unlock();
-
- const char getManagedObjectsDBusSignature[] = "a{oa{sa{sv}}}";
- DBusMessage dbusMessageReply = dbusMessage.createMethodReturn(getManagedObjectsDBusSignature);
- DBusOutputStream outStream(dbusMessageReply);
-
- outStream << resultObjectPathsInterfacesAndPropertiesDict;
- outStream.flush();
- return dbusConnection->sendDBusMessage(dbusMessageReply);
+ return isDBusInterfaceHandlerAdded;
}
bool DBusObjectManager::onIntrospectableInterfaceDBusMessage(const DBusMessage& dbusMessage) {
@@ -162,14 +191,14 @@ bool DBusObjectManager::onIntrospectableInterfaceDBusMessage(const DBusMessage&
const DBusInterfaceHandlerPath& handlerPath = registeredObjectsIterator.first;
const std::string& dbusObjectPath = handlerPath.first;
const std::string& dbusInterfaceName = handlerPath.second;
- DBusStubAdapter* dbusStubAdapter = registeredObjectsIterator.second;
+ DBusInterfaceHandler* dbusStubAdapterBase = registeredObjectsIterator.second;
std::vector<std::string> elems = CommonAPI::split(dbusObjectPath, '/');
if (dbusMessage.hasObjectPath(dbusObjectPath)) {
foundRegisteredObjects = true;
xmlData << "<interface name=\"" << dbusInterfaceName << "\">\n"
- << dbusStubAdapter->getMethodsDBusIntrospectionXmlData() << "\n"
+ << dbusStubAdapterBase->getMethodsDBusIntrospectionXmlData() << "\n"
"</interface>\n";
nodeSet.insert(elems.back());
//break;
diff --git a/src/CommonAPI/DBus/DBusObjectManager.h b/src/CommonAPI/DBus/DBusObjectManager.h
index 55bcb62..130ac0c 100644
--- a/src/CommonAPI/DBus/DBusObjectManager.h
+++ b/src/CommonAPI/DBus/DBusObjectManager.h
@@ -14,46 +14,53 @@
#include "DBusProxyConnection.h"
#include "DBusMessage.h"
+#include "DBusObjectManagerStub.h"
+
namespace CommonAPI {
namespace DBus {
-// objectPath, interfaceName
-typedef std::pair<std::string, std::string> DBusInterfaceHandlerPath;
-typedef DBusInterfaceHandlerPath DBusInterfaceHandlerToken;
-
class DBusStubAdapter;
+class DBusInterfaceHandler;
class DBusObjectManager {
public:
DBusObjectManager(const std::shared_ptr<DBusProxyConnection>&);
~DBusObjectManager();
- void init();
-
- DBusInterfaceHandlerToken registerDBusStubAdapter(const std::string& objectPath,
- const std::string& interfaceName,
- DBusStubAdapter* dbusStubAdapter);
-
- void unregisterDBusStubAdapter(const DBusInterfaceHandlerToken& dbusInterfaceHandlerToken);
+ bool registerDBusStubAdapter(DBusStubAdapter* dbusStubAdapter);
+ bool unregisterDBusStubAdapter(DBusStubAdapter* dbusStubAdapter);
bool handleMessage(const DBusMessage&);
+ inline DBusObjectManagerStub& getRootDBusObjectManagerStub();
private:
- void addLibdbusObjectPathHandler(const std::string& objectPath);
- void removeLibdbusObjectPathHandler(const std::string& objectPath);
+ // objectPath, interfaceName
+ typedef std::pair<std::string, std::string> DBusInterfaceHandlerPath;
+
+ bool addDBusInterfaceHandler(const DBusInterfaceHandlerPath& dbusInterfaceHandlerPath,
+ DBusInterfaceHandler* dbusInterfaceHandler);
+
+ bool removeDBusInterfaceHandler(const DBusInterfaceHandlerPath& dbusInterfaceHandlerPath,
+ DBusInterfaceHandler* dbusInterfaceHandler);
- bool onObjectManagerInterfaceDBusMessage(const DBusMessage& callMessage);
bool onIntrospectableInterfaceDBusMessage(const DBusMessage& callMessage);
- typedef std::unordered_map<DBusInterfaceHandlerPath, DBusStubAdapter*> DBusRegisteredObjectsTable;
+ typedef std::unordered_map<DBusInterfaceHandlerPath, DBusInterfaceHandler*> DBusRegisteredObjectsTable;
DBusRegisteredObjectsTable dbusRegisteredObjectsTable_;
+ DBusObjectManagerStub rootDBusObjectManagerStub_;
+
std::weak_ptr<DBusProxyConnection> dbusConnection_;
std::recursive_mutex objectPathLock_;
};
+
+DBusObjectManagerStub& DBusObjectManager::getRootDBusObjectManagerStub() {
+ return rootDBusObjectManagerStub_;
+}
+
} // namespace DBus
} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusObjectManagerStub.cpp b/src/CommonAPI/DBus/DBusObjectManagerStub.cpp
new file mode 100644
index 0000000..c574998
--- /dev/null
+++ b/src/CommonAPI/DBus/DBusObjectManagerStub.cpp
@@ -0,0 +1,283 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "DBusObjectManagerStub.h"
+#include "DBusStubAdapter.h"
+#include "DBusServicePublisher.h"
+#include "DBusOutputStream.h"
+
+#include <cassert>
+#include <vector>
+
+namespace CommonAPI {
+namespace DBus {
+
+DBusObjectManagerStub::DBusObjectManagerStub(const std::string& dbusObjectPath,
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection) :
+ dbusConnectionWeakPtr_(dbusConnection),
+ dbusObjectPath_(dbusObjectPath) {
+ assert(!dbusObjectPath.empty());
+ assert(dbusObjectPath[0] == '/');
+ assert(dbusConnection);
+}
+
+DBusObjectManagerStub::~DBusObjectManagerStub() {
+ for (auto& dbusObjectPathIterator : registeredDBusObjectPathsMap_) {
+ auto& registeredDBusInterfacesMap = dbusObjectPathIterator.second;
+
+ for (auto& dbusInterfaceIterator : registeredDBusInterfacesMap) {
+ auto managedDBusStubAdapter = dbusInterfaceIterator.second;
+ auto managedDBusStubAdapterServiceAddress = managedDBusStubAdapter->getAddress();
+
+ const bool isServiceUnregistered = DBusServicePublisher::getInstance()->unregisterManagedService(
+ managedDBusStubAdapterServiceAddress);
+ assert(isServiceUnregistered);
+ }
+ }
+}
+
+bool DBusObjectManagerStub::exportDBusStubAdapter(DBusStubAdapter* dbusStubAdapter) {
+ assert(dbusStubAdapter);
+
+ std::lock_guard<std::mutex> dbusObjectManagerStubLock(dbusObjectManagerStubLock_);
+
+ const bool isRegistrationSuccessful = registerDBusStubAdapter(dbusStubAdapter);
+ if (!isRegistrationSuccessful) {
+ return false;
+ }
+
+ auto dbusConnection = dbusConnectionWeakPtr_.lock();
+ if (dbusConnection && dbusConnection->isConnected()) {
+ const bool isDBusSignalEmitted = emitInterfacesAddedSignal(dbusStubAdapter, dbusConnection);
+
+ if (!isDBusSignalEmitted) {
+ unregisterDBusStubAdapter(dbusStubAdapter);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DBusObjectManagerStub::unexportDBusStubAdapter(DBusStubAdapter* dbusStubAdapter) {
+ assert(dbusStubAdapter);
+
+ std::lock_guard<std::mutex> dbusObjectManagerStubLock(dbusObjectManagerStubLock_);
+
+ const bool isRegistrationSuccessful = unregisterDBusStubAdapter(dbusStubAdapter);
+ if (!isRegistrationSuccessful) {
+ return false;
+ }
+
+ auto dbusConnection = dbusConnectionWeakPtr_.lock();
+ if (dbusConnection && dbusConnection->isConnected()) {
+ const bool isDBusSignalEmitted = emitInterfacesRemovedSignal(dbusStubAdapter, dbusConnection);
+
+ if (!isDBusSignalEmitted) {
+ registerDBusStubAdapter(dbusStubAdapter);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DBusObjectManagerStub::isDBusStubAdapterExported(DBusStubAdapter* dbusStubAdapter) {
+ assert(dbusStubAdapter);
+
+ const auto& dbusObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusInterfaceName = dbusStubAdapter->getInterfaceName();
+
+ std::lock_guard<std::mutex> dbusObjectManagerStubLock(dbusObjectManagerStubLock_);
+
+ const auto& registeredDBusObjectPathIterator = registeredDBusObjectPathsMap_.find(dbusObjectPath);
+ const bool isKnownDBusObjectPath = (registeredDBusObjectPathIterator != registeredDBusObjectPathsMap_.end());
+
+ if (!isKnownDBusObjectPath) {
+ return false;
+ }
+
+ auto& registeredDBusInterfacesMap = registeredDBusObjectPathIterator->second;
+ const auto& registeredDBusInterfaceIterator = registeredDBusInterfacesMap.find(dbusInterfaceName);
+ const bool isRegisteredDBusInterfaceName = (registeredDBusInterfaceIterator != registeredDBusInterfacesMap.end());
+
+ if (isRegisteredDBusInterfaceName) {
+ auto registeredDBusStubAdapter = registeredDBusInterfaceIterator->second;
+ assert(registeredDBusStubAdapter == dbusStubAdapter);
+ }
+
+ return isRegisteredDBusInterfaceName;
+}
+
+bool DBusObjectManagerStub::registerDBusStubAdapter(DBusStubAdapter* dbusStubAdapter) {
+ const auto& dbusObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusInterfaceName = dbusStubAdapter->getInterfaceName();
+ const auto& registeredDBusObjectPathIterator = registeredDBusObjectPathsMap_.find(dbusObjectPath);
+ const bool isKnownDBusObjectPath = (registeredDBusObjectPathIterator != registeredDBusObjectPathsMap_.end());
+ bool isRegisterationSuccessful = false;
+
+ if (isKnownDBusObjectPath) {
+ auto& registeredDBusInterfacesMap = registeredDBusObjectPathIterator->second;
+ const auto& registeredDBusInterfaceIterator = registeredDBusInterfacesMap.find(dbusInterfaceName);
+ const bool isDBusInterfaceAlreadyRegistered = (registeredDBusInterfaceIterator != registeredDBusInterfacesMap.end());
+
+ if (!isDBusInterfaceAlreadyRegistered) {
+ const auto& insertResult = registeredDBusInterfacesMap.insert({ dbusInterfaceName, dbusStubAdapter });
+ isRegisterationSuccessful = insertResult.second;
+ }
+ } else {
+ const auto& insertResult = registeredDBusObjectPathsMap_.insert({
+ dbusObjectPath, DBusInterfacesMap({{ dbusInterfaceName, dbusStubAdapter }})
+ });
+ isRegisterationSuccessful = insertResult.second;
+ }
+
+ return isRegisterationSuccessful;
+}
+
+bool DBusObjectManagerStub::unregisterDBusStubAdapter(DBusStubAdapter* dbusStubAdapter) {
+ const auto& dbusObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusInterfaceName = dbusStubAdapter->getInterfaceName();
+ const auto& registeredDBusObjectPathIterator = registeredDBusObjectPathsMap_.find(dbusObjectPath);
+ const bool isKnownDBusObjectPath = (registeredDBusObjectPathIterator != registeredDBusObjectPathsMap_.end());
+
+ if (!isKnownDBusObjectPath) {
+ return false;
+ }
+
+ auto& registeredDBusInterfacesMap = registeredDBusObjectPathIterator->second;
+ const auto& registeredDBusInterfaceIterator = registeredDBusInterfacesMap.find(dbusInterfaceName);
+ const bool isRegisteredDBusInterfaceName = (registeredDBusInterfaceIterator != registeredDBusInterfacesMap.end());
+
+ if (!isRegisteredDBusInterfaceName) {
+ return false;
+ }
+
+ auto registeredDBusStubAdapter = registeredDBusInterfaceIterator->second;
+ assert(registeredDBusStubAdapter == dbusStubAdapter);
+
+ registeredDBusInterfacesMap.erase(registeredDBusInterfaceIterator);
+
+ if (registeredDBusInterfacesMap.empty()) {
+ registeredDBusObjectPathsMap_.erase(registeredDBusObjectPathIterator);
+ }
+
+ return true;
+}
+
+
+bool DBusObjectManagerStub::emitInterfacesAddedSignal(DBusStubAdapter* dbusStubAdapter,
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection) const {
+ assert(dbusConnection);
+ assert(dbusConnection->isConnected());
+
+ const auto& dbusStubObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusStubInterfaceName = dbusStubAdapter->getInterfaceName();
+ DBusMessage dbusSignal = DBusMessage::createSignal(dbusObjectPath_, getInterfaceName(), "InterfacesAdded", "oa{sa{sv}}");
+ DBusOutputStream dbusOutputStream(dbusSignal);
+ DBusInterfacesAndPropertiesDict dbusInterfacesAndPropertiesDict({
+ { dbusStubInterfaceName, DBusPropertiesChangedDict() }
+ });
+
+ if (dbusStubAdapter->hasDBusObjectManagerStub()) {
+ dbusInterfacesAndPropertiesDict.insert({ getInterfaceName(), DBusPropertiesChangedDict() });
+ }
+
+ dbusOutputStream << dbusStubObjectPath;
+ dbusOutputStream << dbusInterfacesAndPropertiesDict;
+ dbusOutputStream.flush();
+
+ const bool dbusSignalEmitted = dbusConnection->sendDBusMessage(dbusSignal);
+ return dbusSignalEmitted;
+}
+
+bool DBusObjectManagerStub::emitInterfacesRemovedSignal(DBusStubAdapter* dbusStubAdapter,
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection) const {
+ assert(dbusConnection);
+ assert(dbusConnection->isConnected());
+
+ const auto& dbusStubObjectPath = dbusStubAdapter->getObjectPath();
+ const auto& dbusStubInterfaceName = dbusStubAdapter->getInterfaceName();
+ DBusMessage dbusSignal = DBusMessage::createSignal(dbusObjectPath_, getInterfaceName(), "InterfacesRemoved", "oas");
+ DBusOutputStream dbusOutputStream(dbusSignal);
+ std::vector<std::string> removedInterfaces({ { dbusStubInterfaceName } });
+
+ if (dbusStubAdapter->hasDBusObjectManagerStub()) {
+ removedInterfaces.push_back(getInterfaceName());
+ }
+
+ dbusOutputStream << dbusStubObjectPath;
+ dbusOutputStream << removedInterfaces;
+ dbusOutputStream.flush();
+
+ const bool dbusSignalEmitted = dbusConnection->sendDBusMessage(dbusSignal);
+ return dbusSignalEmitted;
+}
+
+const char* DBusObjectManagerStub::getMethodsDBusIntrospectionXmlData() const {
+ return "<method name=\"GetManagedObjects\">\n"
+ "<arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n"
+ "</method>\n"
+ "<signal name=\"InterfacesAdded\">\n"
+ "<arg type=\"o\" name=\"object_path\"/>\n"
+ "<arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n"
+ "</signal>\n"
+ "<signal name=\"InterfacesRemoved\">\n"
+ "<arg type=\"o\" name=\"object_path\"/>\n"
+ "<arg type=\"as\" name=\"interfaces\"/>\n"
+ "</signal>";
+}
+
+bool DBusObjectManagerStub::onInterfaceDBusMessage(const DBusMessage& dbusMessage) {
+ auto dbusConnection = dbusConnectionWeakPtr_.lock();
+
+ if (!dbusConnection || !dbusConnection->isConnected()) {
+ return false;
+ }
+
+ if (!dbusMessage.isMethodCallType() || !dbusMessage.hasMemberName("GetManagedObjects")) {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> dbusObjectManagerStubLock(dbusObjectManagerStubLock_);
+ DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+
+ for (const auto& registeredDBusObjectPathIterator : registeredDBusObjectPathsMap_) {
+ const std::string& registeredDBusObjectPath = registeredDBusObjectPathIterator.first;
+ const auto& registeredDBusInterfacesMap = registeredDBusObjectPathIterator.second;
+ DBusInterfacesAndPropertiesDict dbusInterfacesAndPropertiesDict;
+
+ assert(registeredDBusObjectPath.length() > 0);
+ assert(registeredDBusInterfacesMap.size() > 0);
+
+ for (const auto& registeredDBusInterfaceIterator : registeredDBusInterfacesMap) {
+ const std::string& registeredDBusInterfaceName = registeredDBusInterfaceIterator.first;
+ const auto& registeredDBusStubAdapter = registeredDBusInterfaceIterator.second;
+
+ assert(registeredDBusInterfaceName.length() > 0);
+
+ dbusInterfacesAndPropertiesDict.insert({ registeredDBusInterfaceName, DBusPropertiesChangedDict() });
+
+ if (registeredDBusStubAdapter->hasDBusObjectManagerStub()) {
+ dbusInterfacesAndPropertiesDict.insert({ getInterfaceName(), DBusPropertiesChangedDict() });
+ }
+ }
+
+ dbusObjectPathAndInterfacesDict.insert({ registeredDBusObjectPath, std::move(dbusInterfacesAndPropertiesDict) });
+ }
+
+ DBusMessage dbusMessageReply = dbusMessage.createMethodReturn("a{oa{sa{sv}}}");
+ DBusOutputStream dbusOutputStream(dbusMessageReply);
+
+ dbusOutputStream << dbusObjectPathAndInterfacesDict;
+ dbusOutputStream.flush();
+
+ const bool dbusMessageReplySent = dbusConnection->sendDBusMessage(dbusMessageReply);
+ return dbusMessageReplySent;
+}
+
+} // namespace DBus
+} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusObjectManagerStub.h b/src/CommonAPI/DBus/DBusObjectManagerStub.h
new file mode 100644
index 0000000..42db8e3
--- /dev/null
+++ b/src/CommonAPI/DBus/DBusObjectManagerStub.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef COMMONAPI_DBUS_DBUS_FREEDESKTOP_OBJECT_MANAGER_STUB_H_
+#define COMMONAPI_DBUS_DBUS_FREEDESKTOP_OBJECT_MANAGER_STUB_H_
+
+// TODO rename DBusObjectManager to DBusStubManager
+
+// TODO proxy/client logic simply instantiates DBusObjectManagerProxy
+// with the object path of the currently instantiated proxy. That
+// way asking about available instances is a matter of calling
+// getManagedObjects(). Maybe we'll also have to cache within the
+// proxy like the service registry is doing.
+
+// TODO Service Discovery
+// - first detect all objects of a service via the Introspectible interface (like d-feet does).
+// - Introspectible is less efficient than a global ObjectManager but a global ObjectManager
+// conflicts with local ones, so we can't intentionally have one. The tradeoff is efficiency for correctness
+// - Register for all InterfaceAdded and InterfaceRemoved signals on the bus.
+// - Handle double InterfaceAdded and InterfaceRemoved signals properly (ignore them!).
+
+#include "DBusInterfaceHandler.h"
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+namespace CommonAPI {
+namespace DBus {
+
+class DBusStubAdapter;
+
+/**
+ * Stub for standard <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">org.freedesktop.dbus.ObjectManager</a> interface.
+ *
+ * Instantiated within a manager stub and it must hold reference to all registered objects.
+ * Whenever the manager gets destroyed all references to registered objects are lost too.
+ * This duplicates the semantic of the CommonAPI::ServicePublisher class.
+ *
+ * Only one DBusStubAdapter instance could be registered per DBusObjectManagerStub instance.
+ *
+ * The owner of the DBusObjectManagerStub instance must take care of registering and unregistering it.
+ *
+ * Example stub life cycle:
+ * - create CommonAPI::ServicePublisher
+ * - create stub A
+ * - register stub A to CommonAPI::ServicePublisher
+ * - create stub B
+ * - register stub B with stub A as object manager
+ * - drop all references to stub B, stub A keeps a reference to stub B
+ * - drop all references to stub A, CommonAPI::ServicePublisher keeps a reference to stub A
+ * - reference overview: Application > CommonAPI::ServicePublisher > Stub A > Stub B
+ * - drop all references to CommonAPI::ServicePublisher causes all object references to be dropped
+ */
+class DBusObjectManagerStub: public DBusInterfaceHandler {
+ public:
+ // XXX serialization trick: use bool instead of variant since we never serialize it
+ typedef std::unordered_map<std::string, bool> DBusPropertiesChangedDict;
+ typedef std::unordered_map<std::string, DBusPropertiesChangedDict> DBusInterfacesAndPropertiesDict;
+ typedef std::unordered_map<std::string, DBusInterfacesAndPropertiesDict> DBusObjectPathAndInterfacesDict;
+
+ public:
+ DBusObjectManagerStub(const std::string& dbusObjectPath, const std::shared_ptr<DBusProxyConnection>&);
+
+ /**
+ * Unregisters all currently registered DBusStubAdapter instances from the DBusServicePublisher
+ */
+ virtual ~DBusObjectManagerStub();
+
+ /**
+ * Export DBusStubAdapter instance with the current DBusObjectManagerStub instance.
+ *
+ * The DBusStubAdapter must be registered with the DBusServicePublisher!
+ *
+ * On registering a
+ * <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">InsterfaceAdded</a>
+ * signal will be emitted with the DBusObjectManagerStub instance's current D-Bus object path.
+ *
+ * @param dbusStubAdapter a refernce to DBusStubAdapter instance
+ *
+ * @return false if the @a dbusStubAdapter instance was already registered
+ * @return false if sending the InterfaceAdded signal fails
+ *
+ * @see ~DBusObjectManagerStub()
+ * @see CommonAPI::ServicePublisher
+ * @see DBusObjectManager
+ */
+ bool exportDBusStubAdapter(DBusStubAdapter* dbusStubAdapter);
+
+ /**
+ * Unexport DBusStubAdapter instance from this DBusObjectManagerStub instance.
+ *
+ * On unregistering a
+ * <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">InsterfaceRemoved</a>
+ * signal will be emitted with the DBusObjectManagerStub instance's current D-Bus object path.
+ *
+ * @param dbusStubAdapter
+ *
+ * @return false if @a dbusStubAdapter wasn't registered
+ * @return true even if sending the InterfaceRemoved signal fails
+ *
+ * @see exportDBusStubAdapter()
+ */
+ bool unexportDBusStubAdapter(DBusStubAdapter* dbusStubAdapter);
+
+ bool isDBusStubAdapterExported(DBusStubAdapter* dbusStubAdapter);
+
+ inline const std::string& getDBusObjectPath() const;
+ inline static const char* getInterfaceName();
+
+ virtual const char* getMethodsDBusIntrospectionXmlData() const;
+ virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage);
+
+ private:
+ bool registerDBusStubAdapter(DBusStubAdapter* dbusStubAdapter);
+ bool unregisterDBusStubAdapter(DBusStubAdapter* dbusStubAdapter);
+
+ bool emitInterfacesAddedSignal(DBusStubAdapter* dbusStubAdapter,
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection) const;
+
+ bool emitInterfacesRemovedSignal(DBusStubAdapter* dbusStubAdapter,
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection) const;
+
+ std::string dbusObjectPath_;
+ std::weak_ptr<DBusProxyConnection> dbusConnectionWeakPtr_;
+
+ typedef std::unordered_map<std::string, DBusStubAdapter*> DBusInterfacesMap;
+ typedef std::unordered_map<std::string, DBusInterfacesMap> DBusObjectPathsMap;
+ DBusObjectPathsMap registeredDBusObjectPathsMap_;
+
+ std::mutex dbusObjectManagerStubLock_;
+};
+
+
+inline const std::string& DBusObjectManagerStub::getDBusObjectPath() const {
+ return dbusObjectPath_;
+}
+
+const char* DBusObjectManagerStub::getInterfaceName() {
+ return "org.freedesktop.DBus.ObjectManager";
+}
+
+} // namespace DBus
+} // namespace CommonAPI
+
+#endif // COMMONAPI_DBUS_DBUS_FREEDESKTOP_OBJECT_MANAGER_STUB_H_
diff --git a/src/CommonAPI/DBus/DBusProxy.cpp b/src/CommonAPI/DBus/DBusProxy.cpp
index 990021c..cc04a91 100644
--- a/src/CommonAPI/DBus/DBusProxy.cpp
+++ b/src/CommonAPI/DBus/DBusProxy.cpp
@@ -13,6 +13,7 @@
#include "DBusUtils.h"
#include <cassert>
+#include <sstream>
namespace CommonAPI {
@@ -28,12 +29,14 @@ void DBusProxyStatusEvent::onListenerAdded(const CancellableListener& listener)
}
-DBusProxy::DBusProxy(const std::string& commonApiAddress,
+DBusProxy::DBusProxy(const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
const std::string& dbusObjectPath,
const std::shared_ptr<DBusProxyConnection>& dbusConnection):
DBusProxyBase(dbusConnection),
+ factory_(factory),
commonApiServiceId_(split(commonApiAddress, ':')[1]),
commonApiParticipantId_(split(commonApiAddress, ':')[2]),
dbusBusName_(dbusBusName),
@@ -43,12 +46,13 @@ DBusProxy::DBusProxy(const std::string& commonApiAddress,
availabilityStatus_(AvailabilityStatus::UNKNOWN),
interfaceVersionAttribute_(*this, "getInterfaceVersion", "uu"),
dbusServiceRegistry_(dbusConnection->getDBusServiceRegistry()) {
+}
- const std::string commonApiDomain = split(commonApiAddress, ':')[0];
- assert(commonApiDomain == "local");
-
+void DBusProxy::init() {
+ std::stringstream ss;
+ ss << "local:" << commonApiServiceId_ << ":" << commonApiParticipantId_;
dbusServiceRegistrySubscription_ = dbusServiceRegistry_->subscribeAvailabilityListener(
- commonApiAddress,
+ ss.str(),
std::bind(&DBusProxy::onDBusServiceInstanceStatus, this, std::placeholders::_1));
}
diff --git a/src/CommonAPI/DBus/DBusProxy.h b/src/CommonAPI/DBus/DBusProxy.h
index 128e1c1..3ee3f68 100644
--- a/src/CommonAPI/DBus/DBusProxy.h
+++ b/src/CommonAPI/DBus/DBusProxy.h
@@ -15,6 +15,7 @@
#include "DBusProxyBase.h"
#include "DBusAttribute.h"
#include "DBusServiceRegistry.h"
+#include "DBusFactory.h"
#include <functional>
#include <memory>
@@ -39,7 +40,8 @@ class DBusProxyStatusEvent: public ProxyStatusEvent {
class DBusProxy: public DBusProxyBase {
public:
- DBusProxy(const std::string& commonApiAddress,
+ DBusProxy(const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
const std::string& dbusObjectPath,
@@ -70,14 +72,15 @@ class DBusProxy: public DBusProxyBase {
DBusProxyConnection::DBusSignalHandler* dbusSignalHandler);
void unsubsribeFromSelectiveBroadcast(const std::string& eventName,
DBusProxyConnection::DBusSignalHandlerToken subscription);
+
+ void init();
private:
DBusProxy(const DBusProxy&) = delete;
SubscriptionStatus onDBusServiceInstanceStatus(const AvailabilityStatus& availabilityStatus);
DBusProxyStatusEvent dbusProxyStatusEvent_;
- DBusServiceRegistry::Subscription dbusServiceRegistrySubscription_;
- DBusServiceStatusEvent::Subscription dbusServiceStatusEventSubscription_;
+ DBusServiceRegistry::DBusServiceSubscription dbusServiceRegistrySubscription_;
AvailabilityStatus availabilityStatus_;
DBusReadonlyAttribute<InterfaceVersionAttribute> interfaceVersionAttribute_;
@@ -90,6 +93,8 @@ class DBusProxy: public DBusProxyBase {
const std::string dbusBusName_;
const std::string dbusObjectPath_;
const std::string dbusInterfaceName_;
+
+ const std::shared_ptr<DBusFactory>& factory_;
};
diff --git a/src/CommonAPI/DBus/DBusProxyBase.h b/src/CommonAPI/DBus/DBusProxyBase.h
index ea79a2a..0911319 100644
--- a/src/CommonAPI/DBus/DBusProxyBase.h
+++ b/src/CommonAPI/DBus/DBusProxyBase.h
@@ -56,6 +56,9 @@ class DBusProxyBase: public virtual CommonAPI::Proxy {
const bool justAddFilter = false);
inline bool removeSignalMemberHandler(const DBusProxyConnection::DBusSignalHandlerToken& dbusSignalHandlerToken);
+
+ virtual void init() = 0;
+
protected:
const std::string commonApiDomain_;
diff --git a/src/CommonAPI/DBus/DBusProxyConnection.h b/src/CommonAPI/DBus/DBusProxyConnection.h
index bce9e37..4f17512 100644
--- a/src/CommonAPI/DBus/DBusProxyConnection.h
+++ b/src/CommonAPI/DBus/DBusProxyConnection.h
@@ -16,7 +16,6 @@
#include "DBusMessage.h"
#include "DBusFunctionalHash.h"
-#include "DBusServiceStatusEvent.h"
#include <CommonAPI/types.h>
#include <CommonAPI/Attribute.h>
@@ -107,6 +106,11 @@ class DBusProxyConnection {
DBusProxy* callingProxy) = 0;
virtual bool removeSignalMemberHandler(const DBusSignalHandlerToken& dbusSignalHandlerToken) = 0;
+ virtual bool addObjectManagerSignalMemberHandler(const std::string& dbusBusName,
+ DBusSignalHandler* dbusSignalHandler) = 0;
+ virtual bool removeObjectManagerSignalMemberHandler(const std::string& dbusBusName,
+ DBusSignalHandler* dbusSignalHandler) = 0;
+
virtual const std::shared_ptr<DBusServiceRegistry> getDBusServiceRegistry() = 0;
virtual const std::shared_ptr<DBusObjectManager> getDBusObjectManager() = 0;
diff --git a/src/CommonAPI/DBus/DBusProxyHelper.h b/src/CommonAPI/DBus/DBusProxyHelper.h
index 4a40602..da8896f 100644
--- a/src/CommonAPI/DBus/DBusProxyHelper.h
+++ b/src/CommonAPI/DBus/DBusProxyHelper.h
@@ -32,72 +32,74 @@ class DBusProxy;
template< class, class >
struct DBusProxyHelper;
-template <
- template <class...> class _In, class... _InArgs,
- template <class...> class _Out, class... _OutArgs>
-struct DBusProxyHelper<_In<_InArgs...>, _Out<_OutArgs...>> {
- template <typename _DBusProxy = DBusProxy>
- static void callMethod(const _DBusProxy& dbusProxy,
- const char* methodName,
- const char* methodSignature,
- const _InArgs&... inArgs,
- CommonAPI::CallStatus& callStatus) {
- if (dbusProxy.isAvailableBlocking()) {
-
- DBusMessage dbusMessage = dbusProxy.createMethodCall(methodName, methodSignature);
-
- if (sizeof...(_InArgs) > 0) {
- DBusOutputStream outputStream(dbusMessage);
- const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...);
- if (!success) {
- callStatus = CallStatus::OUT_OF_MEMORY;
- return;
- }
- outputStream.flush();
- }
-
- const bool dbusMessageSent = dbusProxy.getDBusConnection()->sendDBusMessage(dbusMessage);
- callStatus = dbusMessageSent ? CallStatus::SUCCESS : CallStatus::OUT_OF_MEMORY;
- } else {
- callStatus = CallStatus::NOT_AVAILABLE;
- }
- }
-
- template <typename _DBusProxy = DBusProxy>
- static void callMethodWithReply(
- const _DBusProxy& dbusProxy,
- DBusMessage& dbusMethodCall,
- const _InArgs&... inArgs,
- CommonAPI::CallStatus& callStatus,
- _OutArgs&... outArgs) {
-
- if (sizeof...(_InArgs) > 0) {
- DBusOutputStream outputStream(dbusMethodCall);
- const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...);
- if (!success) {
- callStatus = CallStatus::OUT_OF_MEMORY;
- return;
- }
- outputStream.flush();
- }
-
- DBusError dbusError;
- DBusMessage dbusMessageReply = dbusProxy.getDBusConnection()->sendDBusMessageWithReplyAndBlock(dbusMethodCall, dbusError);
- if (dbusError || !dbusMessageReply.isMethodReturnType()) {
- callStatus = CallStatus::REMOTE_ERROR;
- return;
- }
-
- if (sizeof...(_OutArgs) > 0) {
- DBusInputStream inputStream(dbusMessageReply);
- const bool success = DBusSerializableArguments<_OutArgs...>::deserialize(inputStream, outArgs...);
- if (!success) {
- callStatus = CallStatus::REMOTE_ERROR;
- return;
- }
- }
- callStatus = CallStatus::SUCCESS;
- }
+template<
+ template<class ...> class _In, class... _InArgs,
+ template <class...> class _Out, class... _OutArgs>
+ struct DBusProxyHelper<_In<_InArgs...>, _Out<_OutArgs...>> {
+
+ template <typename _DBusProxy = DBusProxy>
+ static void callMethod(const _DBusProxy& dbusProxy,
+ const char* methodName,
+ const char* methodSignature,
+ const _InArgs&... inArgs,
+ CommonAPI::CallStatus& callStatus) {
+
+ if (dbusProxy.isAvailableBlocking()) {
+
+ DBusMessage dbusMessage = dbusProxy.createMethodCall(methodName, methodSignature);
+
+ if (sizeof...(_InArgs) > 0) {
+ DBusOutputStream outputStream(dbusMessage);
+ const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream,inArgs...);
+ if (!success) {
+ callStatus = CallStatus::OUT_OF_MEMORY;
+ return;
+ }
+ outputStream.flush();
+ }
+
+ const bool dbusMessageSent = dbusProxy.getDBusConnection()->sendDBusMessage(dbusMessage);
+ callStatus = dbusMessageSent ? CallStatus::SUCCESS : CallStatus::OUT_OF_MEMORY;
+ } else {
+ callStatus = CallStatus::NOT_AVAILABLE;
+ }
+ }
+
+ template <typename _DBusProxy = DBusProxy>
+ static void callMethodWithReply(
+ const _DBusProxy& dbusProxy,
+ DBusMessage& dbusMethodCall,
+ const _InArgs&... inArgs,
+ CommonAPI::CallStatus& callStatus,
+ _OutArgs&... outArgs) {
+
+ if (sizeof...(_InArgs) > 0) {
+ DBusOutputStream outputStream(dbusMethodCall);
+ const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...);
+ if (!success) {
+ callStatus = CallStatus::OUT_OF_MEMORY;
+ return;
+ }
+ outputStream.flush();
+ }
+
+ DBusError dbusError;
+ DBusMessage dbusMessageReply = dbusProxy.getDBusConnection()->sendDBusMessageWithReplyAndBlock(dbusMethodCall, dbusError);
+ if (dbusError || !dbusMessageReply.isMethodReturnType()) {
+ callStatus = CallStatus::REMOTE_ERROR;
+ return;
+ }
+
+ if (sizeof...(_OutArgs) > 0) {
+ DBusInputStream inputStream(dbusMessageReply);
+ const bool success = DBusSerializableArguments<_OutArgs...>::deserialize(inputStream, outArgs...);
+ if (!success) {
+ callStatus = CallStatus::REMOTE_ERROR;
+ return;
+ }
+ }
+ callStatus = CallStatus::SUCCESS;
+ }
template <typename _DBusProxy = DBusProxy>
static void callMethodWithReply(
@@ -123,114 +125,155 @@ struct DBusProxyHelper<_In<_InArgs...>, _Out<_OutArgs...>> {
}
}
- template <typename _DBusProxy = DBusProxy>
- static void callMethodWithReply(
- const _DBusProxy& dbusProxy,
- const char* methodName,
- const char* methodSignature,
- const _InArgs&... inArgs,
- CommonAPI::CallStatus& callStatus,
- _OutArgs&... outArgs) {
+ template <typename _DBusProxy = DBusProxy>
+ static void callMethodWithReply(
+ const _DBusProxy& dbusProxy,
+ const char* dbusInterfaceName,
+ const char* methodName,
+ const char* methodSignature,
+ const _InArgs&... inArgs,
+ CommonAPI::CallStatus& callStatus,
+ _OutArgs&... outArgs) {
+ callMethodWithReply(
+ dbusProxy,
+ dbusProxy.getDBusBusName().c_str(),
+ dbusProxy.getDBusObjectPath().c_str(),
+ dbusInterfaceName,
+ methodName,
+ methodSignature,
+ inArgs...,
+ callStatus,
+ outArgs...);
+ }
- if (dbusProxy.isAvailableBlocking()) {
+ template <typename _DBusProxy = DBusProxy>
+ static void callMethodWithReply(
+ const _DBusProxy& dbusProxy,
+ const char* methodName,
+ const char* methodSignature,
+ const _InArgs&... inArgs,
+ CommonAPI::CallStatus& callStatus,
+ _OutArgs&... outArgs) {
- DBusMessage dbusMethodCall = dbusProxy.createMethodCall(methodName, methodSignature);
+ if (dbusProxy.isAvailableBlocking()) {
- callMethodWithReply(dbusProxy, dbusMethodCall, inArgs..., callStatus, outArgs...);
+ DBusMessage dbusMethodCall = dbusProxy.createMethodCall(methodName, methodSignature);
- } else {
- callStatus = CallStatus::NOT_AVAILABLE;
- }
- }
+ callMethodWithReply(dbusProxy, dbusMethodCall, inArgs..., callStatus, outArgs...);
- template <typename _DBusProxy = DBusProxy, typename _AsyncCallback>
- static std::future<CallStatus> callMethodAsync(
- const _DBusProxy& dbusProxy,
- const char* methodName,
- const char* methodSignature,
- const _InArgs&... inArgs,
- _AsyncCallback asyncCallback) {
- if (dbusProxy.isAvailable()) {
- DBusMessage dbusMethodCall = dbusProxy.createMethodCall(methodName, methodSignature);
+ } else {
+ callStatus = CallStatus::NOT_AVAILABLE;
+ }
+ }
+
+ template <typename _DBusProxy = DBusProxy, typename _AsyncCallback>
+ static std::future<CallStatus> callMethodAsync(
+ const _DBusProxy& dbusProxy,
+ const char* methodName,
+ const char* methodSignature,
+ const _InArgs&... inArgs,
+ _AsyncCallback asyncCallback) {
+ if (dbusProxy.isAvailable()) {
+ DBusMessage dbusMethodCall = dbusProxy.createMethodCall(methodName, methodSignature);
+
+ return callMethodAsync(dbusProxy, dbusMethodCall, inArgs..., asyncCallback);
+
+ } else {
- return callMethodAsync(dbusProxy, dbusMethodCall, inArgs..., asyncCallback);
+ CallStatus callStatus = CallStatus::NOT_AVAILABLE;
- } else {
+ callCallbackOnNotAvailable(asyncCallback, typename make_sequence<sizeof...(_OutArgs)>::type());
- CallStatus callStatus = CallStatus::NOT_AVAILABLE;
+ std::promise<CallStatus> promise;
+ promise.set_value(callStatus);
+ return promise.get_future();
+ }
- callCallbackOnNotAvailable(asyncCallback, typename make_sequence<sizeof...(_OutArgs)>::type());
+ }
- std::promise<CallStatus> promise;
- promise.set_value(callStatus);
- return promise.get_future();
- }
+ template <typename _DBusProxy = DBusProxy, typename _AsyncCallback>
+ static std::future<CallStatus> callMethodAsync(
+ const _DBusProxy& dbusProxy,
+ const char* dbusInterfaceName,
+ const char* methodName,
+ const char* methodSignature,
+ const _InArgs&... inArgs,
+ _AsyncCallback asyncCallback) {
- }
+ callMethodAsync(
+ dbusProxy,
+ dbusProxy.getDBusBusName().c_str(),
+ dbusProxy.getDBusObjectPath().c_str(),
+ dbusInterfaceName,
+ methodName,
+ methodSignature,
+ inArgs...,
+ asyncCallback);
+ }
template <typename _DBusProxy = DBusProxy, typename _AsyncCallback>
static std::future<CallStatus> callMethodAsync(
- const _DBusProxy& dbusProxy,
- const char* busName,
- const char* objPath,
- const char* interfaceName,
- const char* methodName,
- const char* methodSignature,
- const _InArgs&... inArgs,
- _AsyncCallback asyncCallback) {
+ const _DBusProxy& dbusProxy,
+ const char* busName,
+ const char* objPath,
+ const char* interfaceName,
+ const char* methodName,
+ const char* methodSignature,
+ const _InArgs&... inArgs,
+ _AsyncCallback asyncCallback) {
if (dbusProxy.isAvailable()) {
- DBusMessage dbusMethodCall = DBusMessage::createMethodCall(
- busName,
- objPath,
- interfaceName,
- methodName,
- methodSignature);
- return callMethodAsync(dbusProxy, dbusMethodCall, inArgs..., asyncCallback);
+ DBusMessage dbusMethodCall = DBusMessage::createMethodCall(
+ busName,
+ objPath,
+ interfaceName,
+ methodName,
+ methodSignature);
+ return callMethodAsync(dbusProxy, dbusMethodCall, inArgs..., asyncCallback);
} else {
- CallStatus callStatus = CallStatus::NOT_AVAILABLE;
+ CallStatus callStatus = CallStatus::NOT_AVAILABLE;
+
+ callCallbackOnNotAvailable(asyncCallback, typename make_sequence<sizeof...(_OutArgs)>::type());
+
+ std::promise<CallStatus> promise;
+ promise.set_value(callStatus);
+ return promise.get_future();
+ }
+ }
- callCallbackOnNotAvailable(asyncCallback, typename make_sequence<sizeof...(_OutArgs)>::type());
- std::promise<CallStatus> promise;
- promise.set_value(callStatus);
- return promise.get_future();
+ template <typename _DBusProxy = DBusProxy, typename _AsyncCallback>
+ static std::future<CallStatus> callMethodAsync(
+ const _DBusProxy& dbusProxy,
+ DBusMessage& dbusMessage,
+ const _InArgs&... inArgs,
+ _AsyncCallback asyncCallback) {
+
+ if (sizeof...(_InArgs) > 0) {
+ DBusOutputStream outputStream(dbusMessage);
+ const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...);
+ if (!success) {
+ std::promise<CallStatus> promise;
+ promise.set_value(CallStatus::OUT_OF_MEMORY);
+ return promise.get_future();
}
- }
-
-
- template <typename _DBusProxy = DBusProxy, typename _AsyncCallback>
- static std::future<CallStatus> callMethodAsync(
- const _DBusProxy& dbusProxy,
- DBusMessage& dbusMessage,
- const _InArgs&... inArgs,
- _AsyncCallback asyncCallback) {
-
- if (sizeof...(_InArgs) > 0) {
- DBusOutputStream outputStream(dbusMessage);
- const bool success = DBusSerializableArguments<_InArgs...>::serialize(outputStream, inArgs...);
- if (!success) {
- std::promise<CallStatus> promise;
- promise.set_value(CallStatus::OUT_OF_MEMORY);
- return promise.get_future();
- }
- outputStream.flush();
- }
-
- return dbusProxy.getDBusConnection()->sendDBusMessageWithReplyAsync(
- dbusMessage,
- DBusProxyAsyncCallbackHandler<_OutArgs...>::create(std::move(asyncCallback)));
-
- }
-
- template <int... _ArgIndices>
- static void callCallbackOnNotAvailable(std::function<void(CallStatus, _OutArgs...)> callback,
- index_sequence<_ArgIndices...>) {
-
- std::tuple<_OutArgs...> argTuple;
- const CallStatus callstatus = CallStatus::NOT_AVAILABLE;
- callback(callstatus, std::move(std::get<_ArgIndices>(argTuple))...);
- }
+ outputStream.flush();
+ }
+
+ return dbusProxy.getDBusConnection()->sendDBusMessageWithReplyAsync(
+ dbusMessage,
+ DBusProxyAsyncCallbackHandler<_OutArgs...>::create(std::move(asyncCallback)));
+
+ }
+
+ template <int... _ArgIndices>
+ static void callCallbackOnNotAvailable(std::function<void(CallStatus, _OutArgs...)> callback,
+ index_sequence<_ArgIndices...>) {
+
+ std::tuple<_OutArgs...> argTuple;
+ const CallStatus callstatus = CallStatus::NOT_AVAILABLE;
+ callback(callstatus, std::move(std::get<_ArgIndices>(argTuple))...);
+ }
};
} // namespace DBus
diff --git a/src/CommonAPI/DBus/DBusProxyManager.cpp b/src/CommonAPI/DBus/DBusProxyManager.cpp
new file mode 100644
index 0000000..7765532
--- /dev/null
+++ b/src/CommonAPI/DBus/DBusProxyManager.cpp
@@ -0,0 +1,151 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "DBusProxyManager.h"
+#include "DBusAddressTranslator.h"
+
+
+namespace CommonAPI {
+namespace DBus {
+
+
+DBusProxyManager::DBusProxyManager(DBusProxy& dbusProxy,
+ const char* interfaceName,
+ const std::shared_ptr<DBusFactory>& factory) :
+ dbusProxy_(dbusProxy),
+ dbusInstanceAvailabilityStatusEvent_(dbusProxy, interfaceName),
+ factory_(factory),
+ interfaceName_(interfaceName) ,
+ registry_(dbusProxy.getDBusConnection()->getDBusServiceRegistry())
+
+{ }
+
+void DBusProxyManager::instancesAsyncCallback(const CommonAPI::CallStatus& status,
+ const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dict,
+ GetAvailableInstancesCallback& call) {
+ std::vector<std::string> returnVec;
+ if (status == CommonAPI::CallStatus::SUCCESS) {
+ translateCommonApiAddresses(dict, returnVec);
+ }
+ call(status, returnVec);
+}
+
+void DBusProxyManager::getAvailableInstances(CommonAPI::CallStatus& callStatus, std::vector<std::string>& availableInstances) {
+ DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+
+ DBusProxyHelper<DBusSerializableArguments<>,
+ DBusSerializableArguments<DBusObjectManagerStub::DBusObjectPathAndInterfacesDict> >::callMethodWithReply(
+ dbusProxy_,
+ DBusObjectManagerStub::getInterfaceName(),
+ "GetManagedObjects",
+ "a{oa{sa{sv}}}",
+ callStatus,
+ dbusObjectPathAndInterfacesDict);
+
+
+
+ if (callStatus == CallStatus::SUCCESS) {
+ translateCommonApiAddresses(dbusObjectPathAndInterfacesDict, availableInstances);
+ }
+}
+
+std::future<CallStatus> DBusProxyManager::getAvailableInstancesAsync(GetAvailableInstancesCallback callback) {
+ return CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<>,
+ CommonAPI::DBus::DBusSerializableArguments<
+ DBusObjectManagerStub::DBusObjectPathAndInterfacesDict> >::callMethodAsync(
+ dbusProxy_,
+ DBusObjectManagerStub::getInterfaceName(),
+ "GetManagedObjects",
+ "a{oa{sa{sv}}}",
+ std::move(
+ std::bind(
+ &DBusProxyManager::instancesAsyncCallback,
+ this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ callback)));
+
+}
+
+void DBusProxyManager::getInstanceAvailabilityStatus(const std::string& instanceAddress,
+ CallStatus& callStatus,
+ AvailabilityStatus& availabilityStatus) {
+
+ std::stringstream ss;
+ ss << "local:" << interfaceName_ << ":" << instanceAddress;
+
+ std::string interfaceName;
+ std::string connectionName;
+ std::string objectPath;
+ DBusAddressTranslator::getInstance().searchForDBusAddress(
+ ss.str(),
+ interfaceName,
+ connectionName,
+ objectPath);
+ availabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
+ if (registry_->isServiceInstanceAlive(interfaceName, connectionName, objectPath)) {
+ availabilityStatus = AvailabilityStatus::AVAILABLE;
+ }
+ callStatus = CallStatus::SUCCESS;
+}
+
+SubscriptionStatus DBusProxyManager::instanceAliveAsyncCallback(const AvailabilityStatus& alive, GetInstanceAvailabilityStatusCallback& call, std::shared_ptr<std::promise<CallStatus> >& callStatus) {
+ call(CallStatus::SUCCESS, alive);
+ callStatus->set_value(CallStatus::SUCCESS);
+ return SubscriptionStatus::CANCEL;
+}
+
+std::future<CallStatus> DBusProxyManager::getInstanceAvailabilityStatusAsync(const std::string& instanceAddress,
+ GetInstanceAvailabilityStatusCallback callback) {
+ std::stringstream ss;
+ ss << "local:" << interfaceName_ << ":" << instanceAddress;
+
+
+ std::shared_ptr<std::promise<CallStatus> > promise = std::make_shared<std::promise<CallStatus>>();
+ registry_->subscribeAvailabilityListener(
+ ss.str(),
+ std::bind(&DBusProxyManager::instanceAliveAsyncCallback,
+ this,
+ std::placeholders::_1,
+ callback,
+ promise)
+ );
+
+ return promise->get_future();
+}
+
+DBusProxyManager::InstanceAvailabilityStatusChangedEvent& DBusProxyManager::getInstanceAvailabilityStatusChangedEvent() {
+ return dbusInstanceAvailabilityStatusEvent_;
+}
+
+void DBusProxyManager::translateCommonApiAddresses(const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dbusObjectPathAndInterfacesDict,
+ std::vector<std::string>& commonApiAddresses) {
+ for (const auto& dbusObjectPathIter : dbusObjectPathAndInterfacesDict) {
+ const std::string& dbusObjectPath = dbusObjectPathIter.first;
+ const auto& dbusInterfacesDict = dbusObjectPathIter.second;
+
+ for (const auto& dbusInterfaceIter : dbusInterfacesDict) {
+ const std::string dbusInterfaceName = dbusInterfaceIter.first;
+ std::string commonApiAddress;
+
+ DBusAddressTranslator::getInstance().searchForCommonAddress(
+ dbusInterfaceName,
+ dbusProxy_.getDBusBusName(),
+ dbusObjectPath,
+ commonApiAddress);
+
+ commonApiAddresses.push_back(commonApiAddress);
+ }
+ }
+}
+
+std::shared_ptr<Proxy> DBusProxyManager::createProxy(const std::string& instanceName) {
+ return factory_->createProxy(interfaceName_, instanceName, interfaceName_, "local");
+}
+
+
+} // namespace DBus
+}// namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusProxyManager.h b/src/CommonAPI/DBus/DBusProxyManager.h
new file mode 100644
index 0000000..bbef57f
--- /dev/null
+++ b/src/CommonAPI/DBus/DBusProxyManager.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#error "Only <CommonAPI/CommonAPI.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef COMMONAPI_DBUS_DBUS_PROXY_MANAGER_H_
+#define COMMONAPI_DBUS_DBUS_PROXY_MANAGER_H_
+
+#include <CommonAPI/ProxyManager.h>
+
+#include "DBusProxy.h"
+#include "DBusFactory.h"
+#include "DBusObjectManagerStub.h"
+#include "DBusInstanceAvailabilityStatusChangedEvent.h"
+
+#include <functional>
+#include <future>
+#include <string>
+#include <vector>
+
+
+namespace CommonAPI {
+namespace DBus {
+
+class DBusProxyManager: public ProxyManager {
+ public:
+ DBusProxyManager(DBusProxy& dbusProxy, const char* interfaceName, const std::shared_ptr<DBusFactory>& factory);
+
+ virtual void getAvailableInstances(CommonAPI::CallStatus&, std::vector<std::string>& availableInstances);
+ virtual std::future<CallStatus> getAvailableInstancesAsync(GetAvailableInstancesCallback callback);
+
+ virtual void getInstanceAvailabilityStatus(const std::string& instanceAddress,
+ CallStatus& callStatus,
+ AvailabilityStatus& availabilityStatus);
+
+ virtual std::future<CallStatus> getInstanceAvailabilityStatusAsync(const std::string&,
+ GetInstanceAvailabilityStatusCallback callback);
+
+ virtual InstanceAvailabilityStatusChangedEvent& getInstanceAvailabilityStatusChangedEvent();
+ protected:
+ virtual std::shared_ptr<Proxy> createProxy(const std::string& instanceName);
+ private:
+
+ void instancesAsyncCallback(const CommonAPI::CallStatus& status,
+ const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dict,
+ GetAvailableInstancesCallback& call);
+
+ SubscriptionStatus instanceAliveAsyncCallback(const AvailabilityStatus& alive,
+ GetInstanceAvailabilityStatusCallback& call,
+ std::shared_ptr<std::promise<CallStatus> >& callStatus);
+
+ void translateCommonApiAddresses(const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dbusObjectPathAndInterfacesDict,
+ std::vector<std::string>& commonApiAddresses);
+
+ DBusProxy& dbusProxy_;
+ DBusInstanceAvailabilityStatusChangedEvent dbusInstanceAvailabilityStatusEvent_;
+ const std::shared_ptr<DBusFactory> factory_;
+ const std::shared_ptr<DBusServiceRegistry> registry_;
+ const char* interfaceName_;
+};
+
+} // namespace DBus
+} // namespace CommonAPI
+
+#endif // COMMONAPI_DBUS_DBUS_PROXY_MANAGER_H_
diff --git a/src/CommonAPI/DBus/DBusServicePublisher.cpp b/src/CommonAPI/DBus/DBusServicePublisher.cpp
index a2ff292..77c9647 100644
--- a/src/CommonAPI/DBus/DBusServicePublisher.cpp
+++ b/src/CommonAPI/DBus/DBusServicePublisher.cpp
@@ -6,6 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DBusServicePublisher.h"
+#include "DBusFactory.h"
+
+#include <cassert>
namespace CommonAPI {
@@ -20,20 +23,130 @@ std::shared_ptr<DBusServicePublisher> DBusServicePublisher::getInstance() {
return instance;
}
-bool DBusServicePublisher::registerService(const std::string& serviceAddress, std::shared_ptr<DBusStubAdapter> adapter) {
- return registeredServices_.insert( {serviceAddress, adapter} ).second;
+bool DBusServicePublisher::registerService(const std::shared_ptr<DBusStubAdapter>& dbusStubAdapter) {
+ auto serviceAddress = dbusStubAdapter->getAddress();
+ const auto& dbusConnection = dbusStubAdapter->getDBusConnection();
+ DBusObjectManagerStub& rootDBusObjectManagerStub = dbusConnection->getDBusObjectManager()->getRootDBusObjectManagerStub();
+ const bool isManagedRegistrationSuccessfull = registerManagedService(dbusStubAdapter);
+
+ if (!isManagedRegistrationSuccessfull) {
+ return false;
+ }
+
+ const bool isServiceExportSuccessful = rootDBusObjectManagerStub.exportDBusStubAdapter(dbusStubAdapter.get());
+ if (!isServiceExportSuccessful) {
+ const bool isManagedDeregistrationSuccessfull = unregisterManagedService(serviceAddress);
+ assert(isManagedDeregistrationSuccessfull);
+ }
+
+ return isServiceExportSuccessful;
+}
+
+std::shared_ptr<DBusStubAdapter> DBusServicePublisher::getRegisteredService(const std::string& serviceAddress) {
+ auto registeredServiceIterator = registeredServices_.find(serviceAddress);
+ if (registeredServiceIterator != registeredServices_.end()) {
+ return registeredServiceIterator->second;
+ }
+ return nullptr;
+}
+
+bool DBusServicePublisher::registerService(const std::shared_ptr<StubBase>& stubBase,
+ const char* interfaceId,
+ const std::string& participantId,
+ const std::string& serviceName,
+ const std::string& domain,
+ const std::shared_ptr<Factory>& factory) {
+ auto dbusFactory = std::dynamic_pointer_cast<DBusFactory>(factory);
+ if (!dbusFactory) {
+ return false;
+ }
+
+ auto dbusStubAdapter = dbusFactory->createDBusStubAdapter(stubBase, interfaceId, participantId, serviceName, domain);
+ if (!dbusStubAdapter) {
+ return false;
+ }
+
+ const bool isRegistrationSuccessful = registerService(dbusStubAdapter);
+ return isRegistrationSuccessful;
}
bool DBusServicePublisher::unregisterService(const std::string& serviceAddress) {
- auto foundStubAdapter = registeredServices_.find(serviceAddress);
- if(foundStubAdapter != registeredServices_.end()) {
- std::shared_ptr<DBusStubAdapter> stubAdapter = foundStubAdapter->second;
- stubAdapter->deinit();
- return registeredServices_.erase(serviceAddress);
+ auto registeredServiceIterator = registeredServices_.find(serviceAddress);
+ const bool isServiceAddressRegistered = (registeredServiceIterator != registeredServices_.end());
+
+ if (!isServiceAddressRegistered) {
+ return false;
}
- return false;
+
+ const auto& registeredDBusStubAdapter = registeredServiceIterator->second;
+ const auto& dbusConnection = registeredDBusStubAdapter->getDBusConnection();
+ auto& rootDBusObjectManagerStub = dbusConnection->getDBusObjectManager()->getRootDBusObjectManagerStub();
+ const bool isRootService = rootDBusObjectManagerStub.isDBusStubAdapterExported(registeredDBusStubAdapter.get());
+ registeredDBusStubAdapter->deactivateManagedInstances();
+ if (isRootService) {
+ const bool isUnexportSuccessfull = rootDBusObjectManagerStub.unexportDBusStubAdapter(registeredDBusStubAdapter.get());
+ assert(isUnexportSuccessfull);
+
+ unregisterManagedService(registeredServiceIterator);
+ }
+
+ return isRootService;
}
+bool DBusServicePublisher::registerManagedService(const std::shared_ptr<DBusStubAdapter>& managedDBusStubAdapter) {
+ auto serviceAddress = managedDBusStubAdapter->getAddress();
+ const auto& insertResult = registeredServices_.insert( {serviceAddress, managedDBusStubAdapter} );
+ const auto& insertIter = insertResult.first;
+ const bool& isInsertSuccessful = insertResult.second;
+
+ if (!isInsertSuccessful) {
+ return false;
+ }
+
+ const auto& dbusConnection = managedDBusStubAdapter->getDBusConnection();
+ const auto dbusObjectManager = dbusConnection->getDBusObjectManager();
+ const bool isDBusObjectRegistrationSuccessful = dbusObjectManager->registerDBusStubAdapter(managedDBusStubAdapter.get());
+ if (!isDBusObjectRegistrationSuccessful) {
+ registeredServices_.erase(insertIter);
+ return false;
+ }
+
+ const auto& dbusServiceName = managedDBusStubAdapter->getDBusName();
+ const bool isServiceNameAcquired = dbusConnection->requestServiceNameAndBlock(dbusServiceName);
+ if (!isServiceNameAcquired) {
+ const bool isDBusObjectDeregistrationSuccessful = dbusObjectManager->unregisterDBusStubAdapter(managedDBusStubAdapter.get());
+ assert(isDBusObjectDeregistrationSuccessful);
+
+ registeredServices_.erase(insertIter);
+ }
+
+ return isServiceNameAcquired;
+}
+
+bool DBusServicePublisher::unregisterManagedService(const std::string& serviceAddress) {
+ auto registeredServiceIterator = registeredServices_.find(serviceAddress);
+ const bool isServiceAddressRegistered = (registeredServiceIterator != registeredServices_.end());
+
+ if (isServiceAddressRegistered) {
+ unregisterManagedService(registeredServiceIterator);
+ }
+
+ return isServiceAddressRegistered;
+}
+
+void DBusServicePublisher::unregisterManagedService(DBusServicesMap::iterator& managedServiceIterator) {
+ const auto& registeredDbusStubAdapter = managedServiceIterator->second;
+ const auto& dbusConnection = registeredDbusStubAdapter->getDBusConnection();
+ const auto dbusObjectManager = dbusConnection->getDBusObjectManager();
+ const auto& dbusServiceName = registeredDbusStubAdapter->getDBusName();
+
+ const bool isDBusStubAdapterUnregistered = dbusObjectManager->unregisterDBusStubAdapter(registeredDbusStubAdapter.get());
+ assert(isDBusStubAdapterUnregistered);
+
+ dbusConnection->releaseServiceName(dbusServiceName);
+
+ registeredServices_.erase(managedServiceIterator);
+}
} // namespace DBus
} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusServicePublisher.h b/src/CommonAPI/DBus/DBusServicePublisher.h
index 4559386..f09a6dc 100644
--- a/src/CommonAPI/DBus/DBusServicePublisher.h
+++ b/src/CommonAPI/DBus/DBusServicePublisher.h
@@ -20,6 +20,8 @@
namespace CommonAPI {
namespace DBus {
+class DBusFactory;
+class DBusObjectManagerStub;
class DBusServicePublisher: public ServicePublisher {
public:
@@ -27,12 +29,28 @@ class DBusServicePublisher: public ServicePublisher {
static std::shared_ptr<DBusServicePublisher> getInstance();
- bool registerService(const std::string& serviceAddress, std::shared_ptr<DBusStubAdapter> adapter);
+ bool registerService(const std::shared_ptr<DBusStubAdapter>& dbusStubAdapter);
+ virtual bool unregisterService(const std::string& serviceAddress);
- bool unregisterService(const std::string& serviceAddress);
+ bool registerManagedService(const std::shared_ptr<DBusStubAdapter>& managedDBusStubAdapter);
+ bool unregisterManagedService(const std::string& serviceAddress);
+
+ std::shared_ptr<DBusStubAdapter> getRegisteredService(const std::string&);
+
+ protected:
+ virtual bool registerService(const std::shared_ptr<StubBase>& stubBase,
+ const char* interfaceId,
+ const std::string& participantId,
+ const std::string& serviceName,
+ const std::string& domain,
+ const std::shared_ptr<Factory>& factory);
private:
- std::unordered_map<std::string, std::shared_ptr<DBusStubAdapter>> registeredServices_;
+ typedef std::unordered_map<std::string, std::shared_ptr<DBusStubAdapter>> DBusServicesMap;
+
+ void unregisterManagedService(DBusServicesMap::iterator& managedServiceIterator);
+
+ DBusServicesMap registeredServices_;
};
} // namespace DBus
diff --git a/src/CommonAPI/DBus/DBusServiceRegistry.cpp b/src/CommonAPI/DBus/DBusServiceRegistry.cpp
index 13c28ab..91eaba1 100644
--- a/src/CommonAPI/DBus/DBusServiceRegistry.cpp
+++ b/src/CommonAPI/DBus/DBusServiceRegistry.cpp
@@ -8,397 +8,890 @@
#include "DBusServiceRegistry.h"
#include "DBusDaemonProxy.h"
#include "DBusProxyAsyncCallbackHandler.h"
+#include "DBusUtils.h"
+
+#include <iostream>
+#include <iterator>
namespace CommonAPI {
namespace DBus {
-DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection):
+DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection) :
dbusDaemonProxy_(std::make_shared<CommonAPI::DBus::DBusDaemonProxy>(dbusProxyConnection)),
- dbusNameListStatus_(AvailabilityStatus::UNKNOWN),
- initialized_(false), notificationThread_() {
+ initialized_(false),
+ notificationThread_() {
}
DBusServiceRegistry::~DBusServiceRegistry() {
- if(initialized_) {
- dbusDaemonProxy_->getNameOwnerChangedEvent().unsubscribe(dbusDaemonProxyNameOwnerChangedEventSubscription_);
- dbusDaemonProxy_->getProxyStatusEvent().unsubscribe(dbusDaemonProxyStatusEventSubscription_);
- }
+ if (!initialized_) {
+ return;
+ }
+
+ dbusDaemonProxy_->getNameOwnerChangedEvent().unsubscribe(dbusDaemonProxyNameOwnerChangedEventSubscription_);
+ dbusDaemonProxy_->getProxyStatusEvent().unsubscribe(dbusDaemonProxyStatusEventSubscription_);
+
+ // notify only listeners of resolved services (online > offline)
+ for (auto& dbusServiceListenersIterator : dbusServiceListenersMap) {
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator.second;
+
+ // fulfill all open promises
+ std::promise<DBusRecordState> promiseOnResolve = std::move(dbusServiceListenersRecord.promiseOnResolve);
+ promiseOnResolve.set_value(DBusRecordState::NOT_AVAILABLE);
+
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED) {
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
+ }
+ }
+
+ // remove all object manager signal member handlers
+ for (auto& dbusServiceUniqueNameIterator : dbusUniqueNamesMap_) {
+ const auto& dbusServiceUniqueName = dbusServiceUniqueNameIterator.first;
+
+ auto dbusProxyConnection = dbusDaemonProxy_->getDBusConnection();
+ const bool isSubscriptionCancelled = dbusProxyConnection->removeObjectManagerSignalMemberHandler(
+ dbusServiceUniqueName,
+ this);
+ assert(isSubscriptionCancelled);
+ }
}
void DBusServiceRegistry::init() {
- dbusDaemonProxyStatusEventSubscription_ =
- dbusDaemonProxy_->getProxyStatusEvent().subscribeCancellableListener(
- std::bind(&DBusServiceRegistry::onDBusDaemonProxyStatusEvent, this, std::placeholders::_1));
+ dbusDaemonProxyStatusEventSubscription_ =
+ dbusDaemonProxy_->getProxyStatusEvent().subscribeCancellableListener(
+ std::bind(&DBusServiceRegistry::onDBusDaemonProxyStatusEvent, shared_from_this(), std::placeholders::_1));
+
+ dbusDaemonProxyNameOwnerChangedEventSubscription_ =
+ dbusDaemonProxy_->getNameOwnerChangedEvent().subscribeCancellableListener(
+ std::bind(&DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent,
+ this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
- dbusDaemonProxyNameOwnerChangedEventSubscription_ =
- dbusDaemonProxy_->getNameOwnerChangedEvent().subscribeCancellableListener(
- std::bind(&DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent,
- this,
- std::placeholders::_1,
- std::placeholders::_2,
- std::placeholders::_3));
- initialized_ = true;
+ fetchAllServiceNames(); // initialize list of registered bus names
+
+ initialized_ = true;
}
-bool DBusServiceRegistry::waitDBusServicesAvailable(std::unique_lock<std::mutex>& lock, std::chrono::milliseconds& timeout) {
- bool dbusServicesStatusIsKnown = (dbusNameListStatus_ == AvailabilityStatus::AVAILABLE);
+DBusServiceRegistry::DBusServiceSubscription DBusServiceRegistry::subscribeAvailabilityListener(const std::string& commonApiAddress,
+ DBusServiceListener serviceListener) {
+ std::string dbusInterfaceName;
+ std::string dbusServiceName;
+ std::string dbusObjectPath;
- if(!dbusServicesStatusIsKnown) {
- typedef std::chrono::high_resolution_clock clock;
- clock::time_point startTimePoint = clock::now();
+ DBusAddressTranslator::getInstance().searchForDBusAddress(
+ commonApiAddress,
+ dbusInterfaceName,
+ dbusServiceName,
+ dbusObjectPath);
- while (!dbusServicesStatusIsKnown && timeout.count() > 0) {
- dbusServicesStatusIsKnown = dbusServiceChanged_.wait_for(
- lock,
- timeout / 10,
- [&]{ return dbusNameListStatus_ == AvailabilityStatus::AVAILABLE; });
+ if (notificationThread_ == std::this_thread::get_id()) {
+ std::cerr << "ERROR: You must not build proxies in callbacks of ProxyStatusEvent."
+ << " Refer to the documentation for suggestions how to avoid this.\n";
+ assert(false);
+ }
- std::chrono::milliseconds elapsedWaitTime =
- std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - startTimePoint);
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ auto& dbusServiceListenersRecord = dbusServiceListenersMap[dbusServiceName];
+ assert(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::AVAILABLE);
+
+ auto& dbusInterfaceNameListenersMap = dbusServiceListenersRecord.dbusObjectPathListenersMap[dbusObjectPath];
+ auto& dbusInterfaceNameListenersRecord = dbusInterfaceNameListenersMap[dbusInterfaceName];
+
+ AvailabilityStatus availabilityStatus = AvailabilityStatus::UNKNOWN;
+
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::UNKNOWN) {
+ dbusInterfaceNameListenersRecord.state = DBusRecordState::UNKNOWN;
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::UNKNOWN) {
+ resolveDBusServiceName(dbusServiceName, dbusServiceListenersRecord);
+ }
+ } else if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::NOT_AVAILABLE) {
+ availabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
+ } else if (dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVING && dbusInterfaceNameListenersRecord.state == DBusRecordState::UNKNOWN) {
+ dbusInterfaceNameListenersRecord.state = resolveDBusInterfaceNameState(
+ dbusInterfaceName,
+ dbusObjectPath,
+ dbusServiceName,
+ dbusServiceListenersRecord);
+ }
- if (elapsedWaitTime > timeout) {
+ if(availabilityStatus == AvailabilityStatus::UNKNOWN) {
+ switch (dbusInterfaceNameListenersRecord.state) {
+ case DBusRecordState::AVAILABLE:
+ availabilityStatus = AvailabilityStatus::AVAILABLE;
+ break;
+ case DBusRecordState::NOT_AVAILABLE:
+ availabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
break;
+ default:
+ availabilityStatus = AvailabilityStatus::UNKNOWN;
+ }
+ }
+
+
+ if (availabilityStatus != AvailabilityStatus::UNKNOWN) {
+ notificationThread_ = std::this_thread::get_id();
+ SubscriptionStatus subscriptionStatus = serviceListener(availabilityStatus);
+ notificationThread_ = std::thread::id();
+
+ if (subscriptionStatus == SubscriptionStatus::CANCEL) {
+ if (dbusInterfaceNameListenersRecord.listenerList.empty()) {
+ dbusInterfaceNameListenersMap.erase(dbusInterfaceName);
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(dbusObjectPath);
+ }
}
+
+ return DBusServiceSubscription();
}
}
- return (dbusNameListStatus_ == AvailabilityStatus::AVAILABLE);
+ dbusInterfaceNameListenersRecord.listenerList.push_front(std::move(serviceListener));
+
+ return dbusInterfaceNameListenersRecord.listenerList.begin();
}
-bool DBusServiceRegistry::isServiceInstanceAlive(const std::string& dbusInterfaceName, const std::string& dbusServiceName, const std::string& dbusObjectPath) {
- if (!dbusDaemonProxy_->isAvailable()) {
- return false;
+void DBusServiceRegistry::unsubscribeAvailabilityListener(const std::string& commonApiAddress,
+ DBusServiceSubscription& listenerSubscription) {
+ std::string dbusInterfaceName;
+ std::string dbusServiceName;
+ std::string dbusObjectPath;
+
+ DBusAddressTranslator::getInstance().searchForDBusAddress(
+ commonApiAddress,
+ dbusInterfaceName,
+ dbusServiceName,
+ dbusObjectPath);
+
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ auto dbusServiceListenersIterator = dbusServiceListenersMap.find(dbusServiceName);
+ const bool isDBusServiceListenersRecordFound = (dbusServiceListenersIterator != dbusServiceListenersMap.end());
+
+ if (!isDBusServiceListenersRecordFound) {
+ return; // already unsubscribed
}
- std::chrono::milliseconds timeout(1000);
- std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator->second;
+ auto dbusObjectPathListenersIterator =
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.find(dbusObjectPath);
+ const bool isDBusObjectPathListenersRecordFound =
+ (dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end());
- if (!waitDBusServicesAvailable(dbusServicesLock, timeout)) {
- return false;
+ if (!isDBusObjectPathListenersRecordFound) {
+ return; // already unsubscribed
}
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
- if (dbusServiceIterator == dbusServices_.end()) {
- return false;
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
+ auto dbusInterfaceNameListenersIterator = dbusInterfaceNameListenersMap.find(dbusInterfaceName);
+ const bool isDBusInterfaceNameListenersRecordFound =
+ (dbusInterfaceNameListenersIterator != dbusInterfaceNameListenersMap.end());
+
+ if (!isDBusInterfaceNameListenersRecordFound) {
+ return; // already unsubscribed
}
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
+ auto& dbusInterfaceNameListenersRecord = dbusInterfaceNameListenersIterator->second;
+
+ dbusInterfaceNameListenersRecord.listenerList.erase(listenerSubscription);
+
+ if (dbusInterfaceNameListenersRecord.listenerList.empty()) {
+ dbusInterfaceNameListenersMap.erase(dbusInterfaceNameListenersIterator);
- if (dbusServiceState == DBusServiceState::AVAILABLE) {
- resolveDBusServiceInstances(dbusServiceIterator);
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(dbusObjectPathListenersIterator);
+ }
}
+}
+
+// d-feet mode until service is found
+bool DBusServiceRegistry::isServiceInstanceAlive(const std::string& dbusInterfaceName,
+ const std::string& dbusServiceName,
+ const std::string& dbusObjectPath) {
+ std::chrono::milliseconds timeout(1000);
+ bool uniqueNameFound = false;
+
+ DBusUniqueNameRecord* dbusUniqueNameRecord = NULL;
+ std::string uniqueName;
- if (dbusServiceState == DBusServiceState::RESOLVING) {
- dbusServiceChanged_.wait_for(
- dbusServicesLock,
- timeout,
- [&] { return dbusServiceState != DBusServiceState::RESOLVING; });
+ dbusServicesMutex_.lock();
+
+ findCachedDbusService(dbusServiceName, &dbusUniqueNameRecord);
+
+ if(dbusUniqueNameRecord != NULL) {
+ uniqueName = dbusUniqueNameRecord->uniqueName;
+ uniqueNameFound = true;
}
- const DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
- auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
+ if(!uniqueNameFound) {
+ DBusServiceListenersRecord dbusServiceListenersRecord;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::RESOLVING;
+
+ dbusServiceListenersRecord.futureOnResolve = dbusServiceListenersRecord.promiseOnResolve.get_future();
+ std::unordered_map<std::string, DBusServiceListenersRecord>::value_type value(dbusServiceName, std::move(dbusServiceListenersRecord));
+ auto insertedDbusServiceListenerRecord = dbusServiceListenersMap.insert(std::move(value));
+
+ if(insertedDbusServiceListenerRecord.second) { // if dbusServiceListenerRecord was inserted, start resolving
+ resolveDBusServiceName(dbusServiceName, dbusServiceListenersMap[dbusServiceName]);
+ }
+
+ dbusServicesMutex_.unlock();
+
+ std::shared_future<DBusRecordState> futureNameResolved = insertedDbusServiceListenerRecord.first->second.futureOnResolve;
+ futureNameResolved.wait_for(timeout);
- if (dbusInstanceIterator != dbusInstanceList.end()) {
- const AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
+ if(futureNameResolved.get() != DBusRecordState::RESOLVED) {
+ return false;
+ }
- return (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE);
+ dbusServicesMutex_.lock();
+ auto dbusServiceListenersMapIterator = dbusServiceListenersMap.find(dbusServiceName);
+
+ if(dbusServiceListenersMapIterator == dbusServiceListenersMap.end()) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ uniqueName = dbusServiceListenersMapIterator->second.uniqueBusName;
+
+ if(uniqueName.empty() || dbusServiceListenersMapIterator->second.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ auto dbusUniqueNameRecordIterator = dbusUniqueNamesMap_.find(uniqueName);
+
+ if(dbusUniqueNameRecordIterator == dbusUniqueNamesMap_.end()) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ dbusUniqueNameRecord = &dbusUniqueNameRecordIterator->second;
}
- return false;
+ dbusServicesMutex_.unlock();
+
+ assert(dbusUniqueNameRecord != NULL);
+
+ auto* dbusObjectPathsCache = &(dbusUniqueNameRecord->dbusObjectPathsCache);
+ auto dbusObjectPathCacheIterator = dbusObjectPathsCache->find(dbusObjectPath);
+
+ DBusObjectPathCache* dbusObjectPathCache = NULL;
+
+
+ if(dbusObjectPathCacheIterator != dbusObjectPathsCache->end()) {
+ dbusObjectPathCache = &(dbusObjectPathCacheIterator->second);
+ }
+ else {
+ // try to resolve object paths
+ DBusObjectPathCache newDbusObjectPathCache;
+ newDbusObjectPathCache.state = DBusRecordState::RESOLVING;
+
+ dbusServicesMutex_.lock();
+ //std::unordered_map<std::string, DBusObjectPathCache>::value_type value(dbusObjectPath, std::move(newDbusObjectPathCache));
+ //auto dbusObjectPathCacheInserted = dbusObjectPathsCache->insert(std::move({dbusObjectPath, std::move(newDbusObjectPathCache)}));
+ auto dbusObjectPathCacheInserted =
+ dbusObjectPathsCache->insert(std::make_pair(dbusObjectPath, std::move(newDbusObjectPathCache)));
+
+ dbusObjectPathCacheIterator = dbusObjectPathsCache->find(dbusObjectPath);
+
+ dbusObjectPathCache = &(dbusObjectPathCacheIterator->second);
+
+ std::future<DBusRecordState> futureObjectPathResolved = dbusObjectPathCache->promiseOnResolve.get_future();
+ dbusServicesMutex_.unlock();
+
+ introspectDBusObjectPath(uniqueName, dbusObjectPath);
+ futureObjectPathResolved.wait_for(timeout);
+ }
+
+ assert(dbusObjectPathCache != NULL);
+
+ dbusServicesMutex_.lock();
+ if(dbusObjectPathCache->state != DBusRecordState::RESOLVED) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ auto dbusInterfaceNamesIterator = dbusObjectPathCache->dbusInterfaceNamesCache.find(dbusInterfaceName);
+ bool result = dbusInterfaceNamesIterator != dbusObjectPathCache->dbusInterfaceNamesCache.end();
+ dbusServicesMutex_.unlock();
+
+ return(result);
}
+void DBusServiceRegistry::fetchAllServiceNames() {
+ if (!dbusDaemonProxy_->isAvailable()) {
+ return;
+ }
-// Go through the list of available services and check their interface lists
-// If a list is still unknown, then send request to the remote object manager and count it as invalid
-// If a list is in acquiring state, then just count it as invalid and skip over it
-// Add all matching valid services to the available service list
-// If the invalid service count is set, then wait upto waitTimeLimit (2 seconds) for the object manager requests to complete
-// If the timeout expires, then go through the list for last time and add everything matching
-// If the timeout didn't expire, then go through the list again and send requests for new UNKNOWN services, then wait again for them to complete
-// Known limitations:
-// - if the method is called before the first "listNames()" call completes, this request will be blocked
-// - if a single one(!) of the addressed services is broken and doesn't respond correctly to non-handled requests, this request will always block for the default 2 seconds (waitTimeLimit)
-// - the method has to be called many times, if you actually want to wait for all services, otherwise you'll always get a partial response. I.e. the more you call this method, the hotter the internal cache gets.
+ CallStatus callStatus;
+ std::vector<std::string> availableServiceNames;
+
+ dbusDaemonProxy_->listNames(callStatus, availableServiceNames);
+
+ if(callStatus == CallStatus::SUCCESS) {
+ for(std::string serviceName : availableServiceNames) {
+ if(isDBusServiceName(serviceName)) {
+ dbusServiceNameMap_[serviceName];
+ }
+ }
+ }
+}
+
+// d-feet mode
std::vector<std::string> DBusServiceRegistry::getAvailableServiceInstances(const std::string& interfaceName,
const std::string& domainName) {
std::vector<std::string> availableServiceInstances;
- if (!dbusDaemonProxy_->isAvailable()) {
- return availableServiceInstances;
- }
+ // resolve all service names
+ for (auto serviceNameIterator = dbusServiceNameMap_.begin();
+ serviceNameIterator != dbusServiceNameMap_.end();
+ serviceNameIterator++) {
- std::chrono::milliseconds timeout(2000);
- std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ std::string serviceName = serviceNameIterator->first;
+ DBusUniqueNameRecord* dbusUniqueNameRecord = serviceNameIterator->second;
- if (!waitDBusServicesAvailable(dbusServicesLock, timeout)) {
- return availableServiceInstances;
+ if(dbusUniqueNameRecord == NULL) {
+ DBusServiceListenersRecord& serviceListenerRecord = dbusServiceListenersMap[serviceName];
+ if(serviceListenerRecord.uniqueBusNameState != DBusRecordState::RESOLVING) {
+ resolveDBusServiceName(serviceName, serviceListenerRecord);
+ }
+ }
}
- size_t dbusServiceResolvingCount = getResolvedServiceInstances(interfaceName, availableServiceInstances);
+ std::mutex mutexResolveAllServices;
+ std::unique_lock<std::mutex> lockResolveAllServices(mutexResolveAllServices);
+ std::chrono::milliseconds timeout(5000);
- if (!dbusServiceResolvingCount) {
- return availableServiceInstances;
- }
+ monitorResolveAllServices_.wait_for(lockResolveAllServices, timeout, [&] {
+ mutexServiceResolveCount.lock();
+ bool finished = servicesToResolve == 0;
+ mutexServiceResolveCount.unlock();
- dbusServiceChanged_.wait(
- dbusServicesLock,
- [&] {
- return getNumResolvingServiceInstances() == 0;
- });
+ return finished;
+ });
- getResolvedServiceInstances(interfaceName, availableServiceInstances);
+ for (auto serviceNameIterator = dbusServiceNameMap_.begin();
+ serviceNameIterator != dbusServiceNameMap_.end();
+ serviceNameIterator++) {
+ std::string serviceName = serviceNameIterator->first;
+ DBusUniqueNameRecord* dbusUniqueNameRecord = serviceNameIterator->second;
+
+ if(dbusUniqueNameRecord != NULL) {
+ if(dbusUniqueNameRecord->objectPathsState == DBusRecordState::UNKNOWN) {
+ DBusObjectPathCache& rootObjectPathCache = dbusUniqueNameRecord->dbusObjectPathsCache["/"];
+ if(rootObjectPathCache.state == DBusRecordState::UNKNOWN) {
+ rootObjectPathCache.state = DBusRecordState::RESOLVING;
+ introspectDBusObjectPath(dbusUniqueNameRecord->uniqueName, "/");
+ }
+ }
+ }
+ }
+
+ std::mutex mutexResolveAllObjectPaths;
+ std::unique_lock<std::mutex> lockResolveAllObjectPaths(mutexResolveAllObjectPaths);
+
+ // TODO: should use the remaining timeout not "used" during wait before
+ monitorResolveAllObjectPaths_.wait_for(lockResolveAllObjectPaths, timeout, [&] {
+ mutexServiceResolveCount.lock();
+ bool finished = objectPathsToResolve == 0;
+ mutexServiceResolveCount.unlock();
+
+ return finished;
+ });
+
+ DBusAddressTranslator& dbusAddressTranslator = DBusAddressTranslator::getInstance();
+
+ for (auto serviceNameIterator = dbusServiceNameMap_.begin();
+ serviceNameIterator != dbusServiceNameMap_.end();
+ serviceNameIterator++) {
+
+ std::string serviceName = serviceNameIterator->first;
+ DBusUniqueNameRecord* dbusUniqueNameRecord = serviceNameIterator->second;
+
+ if(dbusUniqueNameRecord != NULL) {
+ if(dbusUniqueNameRecord->objectPathsState == DBusRecordState::RESOLVED) {
+ for (auto dbusObjectPathCacheIterator = dbusUniqueNameRecord->dbusObjectPathsCache.begin();
+ dbusObjectPathCacheIterator != dbusUniqueNameRecord->dbusObjectPathsCache.end();
+ dbusObjectPathCacheIterator++) {
+ if (dbusObjectPathCacheIterator->second.state == DBusRecordState::RESOLVED) {
+ if (dbusObjectPathCacheIterator->second.dbusInterfaceNamesCache.find(interfaceName)
+ != dbusObjectPathCacheIterator->second.dbusInterfaceNamesCache.end()) {
+ std::string commonApiAddress;
+ dbusAddressTranslator.searchForCommonAddress(interfaceName, serviceName, dbusObjectPathCacheIterator->first, commonApiAddress);
+ availableServiceInstances.push_back(commonApiAddress);
+ }
+ }
+ }
+ }
+ }
+ }
// maybe partial list but it contains everything we know for now
return availableServiceInstances;
}
+//std::vector<std::string> DBusServiceRegistry::getManagedObjects(const std::string& connectionName, const std::string& objectpath) {
+// if (auto iter = dbusServiceNameMap_.find(connectionName) != dbusServiceNameMap_.end()) {
+// DBusUniqueNameRecord* rec = iter->second;
+// if (rec->uniqueName != DBusRecordState::RESOLVED) {
+// return std::vector<std::string>();
+// } else {
+// rec->dbusObjectPathsCache
+// }
+//
+// } else {
+// return std::vector<std::string>();
+// }
+//}
void DBusServiceRegistry::getAvailableServiceInstancesAsync(Factory::GetAvailableServiceInstancesCallback callback,
const std::string& interfaceName,
const std::string& domainName) {
- std::vector<std::string> availableServiceInstances;
+ //Necessary as service discovery might need some time, but the async version of "getAvailableServiceInstances"
+ //shall return without delay.
+ std::thread(
+ [this, callback, interfaceName, domainName](std::shared_ptr<DBusServiceRegistry> selfRef) {
+ auto availableServiceInstances = getAvailableServiceInstances(interfaceName, domainName);
+ callback(availableServiceInstances);
+ }, this->shared_from_this()
+ ).detach();
+}
- if (!dbusDaemonProxy_->isAvailable()) {
- callback(availableServiceInstances);
+SubscriptionStatus DBusServiceRegistry::onSignalDBusMessage(const DBusMessage& dbusMessage) {
+ const std::string& dbusServiceUniqueName = dbusMessage.getSenderName();
+
+ assert(dbusMessage.isSignalType());
+ assert(dbusMessage.hasInterfaceName("org.freedesktop.DBus.ObjectManager"));
+ assert(dbusMessage.hasMemberName("InterfacesAdded") || dbusMessage.hasMemberName("InterfacesRemoved"));
+
+ DBusInputStream dbusInputStream(dbusMessage);
+ std::string dbusObjectPath;
+ std::unordered_set<std::string> dbusInterfaceNames;
+ DBusRecordState dbusInterfaceNameState;
+
+ dbusInputStream >> dbusObjectPath;
+
+ bool added = false;
+
+ if (dbusMessage.hasMemberName("InterfacesAdded")) {
+ added = true;
+ dbusInterfaceNameState = DBusRecordState::AVAILABLE;
+
+ typedef std::unordered_map<std::string, bool> DBusPropertiesChangedDict;
+ typedef std::unordered_map<std::string, DBusPropertiesChangedDict> DBusInterfacesAndPropertiesDict;
+ typedef std::unordered_map<std::string, DBusInterfacesAndPropertiesDict> DBusObjectPathAndInterfacesDict;
+ DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ dbusInputStream >> dbusObjectPathAndInterfacesDict;
+
+ for (auto& dbusInterfaceIterator : dbusObjectPathAndInterfacesDict) {
+ const auto& dbusInterfaceName = dbusInterfaceIterator.first;
+ dbusInterfaceNames.insert(dbusInterfaceName);
+ }
+ } else {
+ std::vector<std::string> removedDBusInterfaceNames;
+
+ dbusInterfaceNameState = DBusRecordState::NOT_AVAILABLE;
+ dbusInputStream >> removedDBusInterfaceNames;
+ std::move(
+ removedDBusInterfaceNames.begin(),
+ removedDBusInterfaceNames.end(),
+ std::inserter(dbusInterfaceNames, dbusInterfaceNames.begin()));
+ }
+
+ if (dbusInputStream.hasError()) {
+ return SubscriptionStatus::RETAIN;
+ }
+
+ if (dbusInterfaceNames.empty()) {
+ return SubscriptionStatus::RETAIN;
}
std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- size_t stillResolvingCount = getResolvedServiceInstances(interfaceName, availableServiceInstances);
+ auto dbusServiceUniqueNameIterator = dbusUniqueNamesMap_.find(dbusServiceUniqueName);
+ const bool isDBusServiceUniqueNameFound = (dbusServiceUniqueNameIterator != dbusUniqueNamesMap_.end());
- if (stillResolvingCount == 0 && !dbusServices_.empty()) {
- callback(availableServiceInstances);
- } else {
- //Necessary as service discovery might need some time, but the async version of "getAvailableServiceInstances"
- //shall return without delay.
- std::thread(
- [this, callback, interfaceName, domainName](std::shared_ptr<DBusServiceRegistry> selfRef) {
- auto availableServiceInstances = getAvailableServiceInstances(interfaceName, domainName);
- callback(availableServiceInstances);
- }, this->shared_from_this()
- ).detach();
+ if (!isDBusServiceUniqueNameFound) {
+ return SubscriptionStatus::CANCEL;
}
-}
+ auto& dbusUniqueNameRecord = dbusServiceUniqueNameIterator->second;
-size_t DBusServiceRegistry::getNumResolvingServiceInstances() {
- size_t dbusServicesResolvingCount = 0;
+ //auto dbusObjectPathIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ //const bool isDBusObjectPathFound = (dbusObjectPathIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end());
- // caller must hold lock
- auto dbusServiceIterator = dbusServices_.begin();
- while (dbusServiceIterator != dbusServices_.end()) {
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
+ /*
+ if (!isDBusObjectPathFound) {
+ return SubscriptionStatus::RETAIN;
+ }
+ */
- switch (dbusServiceState) {
- case DBusServiceState::AVAILABLE:
- dbusServicesResolvingCount++;
- break;
+ DBusObjectPathCache& dbusObjectPathRecord = dbusUniqueNameRecord.dbusObjectPathsCache[dbusObjectPath];
+/*
+ if (isDBusObjectPathFound) {
+ dbusObjectPathRecord = &dbusObjectPathIterator->second;
+ }
+ else
+ {
+ DBusObjectPathCache dbusObjectPathRecord;
+ auto insertionResult = dbusUniqueNameRecord.dbusObjectPathsCache.insert(std::make_pair(dbusObjectPath, std::move(dbusObjectPath)));
+ auto objectPathCacheIterator = insertionResult.first;
+ dbusObjectPathRecord = &(objectPathCacheIterator->second);
+ }
+*/
- case DBusServiceState::RESOLVING:
- dbusServicesResolvingCount++;
- break;
+ if (dbusObjectPathRecord.state != DBusRecordState::RESOLVED) {
+ return SubscriptionStatus::RETAIN;
+ }
- default:
- break;
+ for (const auto& dbusInterfaceName : dbusInterfaceNames) {
+ if (dbusInterfaceNameState == DBusRecordState::AVAILABLE) {
+ dbusObjectPathRecord.dbusInterfaceNamesCache.insert(dbusInterfaceName);
+ } else {
+ dbusObjectPathRecord.dbusInterfaceNamesCache.erase(dbusInterfaceName);
}
-
- dbusServiceIterator++;
}
- return dbusServicesResolvingCount;
+ notifyDBusServiceListeners(dbusUniqueNameRecord, dbusObjectPath, dbusInterfaceNames, dbusInterfaceNameState);
+
+ return SubscriptionStatus::RETAIN;
}
-size_t DBusServiceRegistry::getResolvedServiceInstances(const std::string& dbusInterfaceName, std::vector<std::string>& availableServiceInstances) {
- size_t dbusServicesResolvingCount = 0;
+void DBusServiceRegistry::resolveDBusServiceName(const std::string& dbusServiceName,
+ DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ assert(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED);
+ assert(dbusServiceListenersRecord.uniqueBusName.empty());
- availableServiceInstances.clear();
+ mutexServiceResolveCount.lock();
+ servicesToResolve++;
+ mutexServiceResolveCount.unlock();
- // caller must hold lock
- auto dbusServiceIterator = dbusServices_.begin();
- while (dbusServiceIterator != dbusServices_.end()) {
- const std::string& dbusServiceName = dbusServiceIterator->first;
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- const DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ if (dbusDaemonProxy_->isAvailable()) {
+ dbusDaemonProxy_->getNameOwnerAsync(
+ dbusServiceName,
+ std::bind(
+ &DBusServiceRegistry::onGetNameOwnerCallback,
+ this->shared_from_this(),
+ std::placeholders::_1,
+ std::placeholders::_2,
+ dbusServiceName));
- // count the resolving services and start acquiring the objects for unknown ones
- switch (dbusServiceState) {
- case DBusServiceState::AVAILABLE:
- resolveDBusServiceInstances(dbusServiceIterator);
- dbusServicesResolvingCount++;
- break;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::RESOLVING;
+ }
+}
- case DBusServiceState::RESOLVING:
- case DBusServiceState::RESOLVED:
- if (dbusServiceState == DBusServiceState::RESOLVING) {
- dbusServicesResolvingCount++;
- }
+void DBusServiceRegistry::onGetNameOwnerCallback(const CallStatus& status,
+ std::string dbusServiceUniqueName,
+ const std::string& dbusServiceName) {
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- for (auto& dbusInstanceIterator : dbusInstanceList) {
- const AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator.second.first;
- const std::string& dbusInstanceObjectPath = dbusInstanceIterator.first.first;
- const std::string& dbusInstanceInterfaceName = dbusInstanceIterator.first.second;
+ auto dbusServiceListenerIterator = dbusServiceListenersMap.find(dbusServiceName);
+ const bool isDBusServiceListenerRecordFound = (dbusServiceListenerIterator != dbusServiceListenersMap.end());
- if (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE
- && dbusInstanceInterfaceName == dbusInterfaceName) {
- std::string commonApiAddress;
+ if (!isDBusServiceListenerRecordFound) {
+ return;
+ }
- DBusAddressTranslator::getInstance().searchForCommonAddress(
- dbusInterfaceName,
- dbusServiceName,
- dbusInstanceObjectPath,
- commonApiAddress);
+ DBusServiceListenersRecord& dbusServiceListenersRecord = dbusServiceListenerIterator->second;
- availableServiceInstances.emplace_back(std::move(commonApiAddress));
- }
- }
- break;
+ if (status == CallStatus::SUCCESS) {
+ onDBusServiceAvailable(dbusServiceName, dbusServiceUniqueName);
+ if(dbusServiceListenersRecord.futureOnResolve.valid()) {
+ dbusServiceListenersRecord.promiseOnResolve.set_value(DBusRecordState(dbusServiceListenersRecord.uniqueBusNameState));
+ }
+ } else {
+ // try to fulfill open promises
+ if(dbusServiceListenersRecord.futureOnResolve.valid()) {
+ dbusServiceListenersRecord.promiseOnResolve.set_value(DBusRecordState::NOT_AVAILABLE);
}
- dbusServiceIterator++;
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
}
- return dbusServicesResolvingCount;
+ mutexServiceResolveCount.lock();
+ servicesToResolve--;
+ mutexServiceResolveCount.unlock();
+ monitorResolveAllServices_.notify_all();
}
+DBusServiceRegistry::DBusRecordState DBusServiceRegistry::resolveDBusInterfaceNameState(const std::string& dbusInterfaceName,
+ const std::string& dbusObjectPath,
+ const std::string& dbusServiceName,
+ DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ assert(dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED);
+ assert(!dbusServiceListenersRecord.uniqueBusName.empty());
-DBusServiceRegistry::Subscription DBusServiceRegistry::subscribeAvailabilityListener(const std::string& commonApiAddress,
- DBusServiceListener serviceListener) {
- std::string dbusInterfaceName;
- std::string dbusServiceName;
- std::string dbusObjectPath;
+ auto& dbusServiceUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceListenersRecord.uniqueBusName];
+ assert(!dbusServiceUniqueNameRecord.ownedBusNames.empty());
- DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, dbusInterfaceName, dbusServiceName, dbusObjectPath);
+ auto& dbusObjectPathRecord = getDBusObjectPathCacheReference(
+ dbusObjectPath,
+ dbusServiceListenersRecord.uniqueBusName,
+ dbusServiceUniqueNameRecord);
- if (notificationThread_ == std::this_thread::get_id()) {
- printf("You must not build proxies in callbacks of ProxyStatusEvent.\n"
- "Refer to the documentation for suggestions how to avoid this.\n");
- assert(false);
+ if (dbusObjectPathRecord.state != DBusRecordState::RESOLVED) {
+ return dbusObjectPathRecord.state;
}
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- notificationThread_ = std::this_thread::get_id();
- DBusServiceList::iterator dbusServiceIterator = dbusServices_.find(dbusServiceName);
- // Service not known, so just add it to the list of unkown or definitely not available services
- if (dbusServiceIterator == dbusServices_.end()) {
- DBusServiceState dbusConnectionNameState = DBusServiceState::UNKNOWN;
+ auto dbusInterfaceNameIterator = dbusObjectPathRecord.dbusInterfaceNamesCache.find(dbusInterfaceName);
+ const bool isDBusInterfaceNameFound =
+ (dbusInterfaceNameIterator != dbusObjectPathRecord.dbusInterfaceNamesCache.end());
- // Service is definitely not available if the complete list of available services is known and it is not in there
- if (dbusNameListStatus_ == AvailabilityStatus::AVAILABLE) {
- dbusConnectionNameState = DBusServiceState::RESOLVED;
- }
+ return isDBusInterfaceNameFound ? DBusRecordState::AVAILABLE : DBusRecordState::NOT_AVAILABLE;
+}
+
+
+DBusServiceRegistry::DBusObjectPathCache& DBusServiceRegistry::getDBusObjectPathCacheReference(const std::string& dbusObjectPath,
+ const std::string& dbusServiceUniqueName,
+ DBusUniqueNameRecord& dbusUniqueNameRecord) {
+ const bool isFirstDBusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache.empty();
- std::pair<DBusServiceList::iterator, bool> insertResult = dbusServices_.insert({ dbusServiceName, { dbusConnectionNameState, DBusInstanceList() } });
- assert(insertResult.second);
- dbusServiceIterator = insertResult.first;
+ auto dbusObjectPathCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ if(dbusObjectPathCacheIterator == dbusUniqueNameRecord.dbusObjectPathsCache.end()) {
+ std::unordered_map<std::string, DBusObjectPathCache>::value_type value (dbusObjectPath, DBusObjectPathCache());
+ dbusUniqueNameRecord.dbusObjectPathsCache.insert(std::move(value));
+ dbusObjectPathCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
}
- DBusServiceState& dbusConnectionNameState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ if (isFirstDBusObjectPathCache) {
+ auto dbusProxyConnection = dbusDaemonProxy_->getDBusConnection();
+ const bool isSubscriptionSuccessful = dbusProxyConnection->addObjectManagerSignalMemberHandler(
+ dbusServiceUniqueName,
+ this);
+ assert(isSubscriptionSuccessful);
+ }
- auto dbusInstanceIterator = addDBusServiceInstance(
- dbusInstanceList,
- dbusObjectPath,
- dbusInterfaceName);
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ if (dbusObjectPathCacheIterator->second.state == DBusRecordState::UNKNOWN
+ && introspectDBusObjectPath(dbusServiceUniqueName, dbusObjectPath)) {
+ dbusObjectPathCacheIterator->second.state = DBusRecordState::RESOLVING;
+ }
- if (dbusConnectionNameState == DBusServiceState::RESOLVED
- && dbusInstanceAvailabilityStatus == AvailabilityStatus::UNKNOWN) {
- dbusInstanceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
+ return dbusObjectPathCacheIterator->second;
+}
+
+void DBusServiceRegistry::releaseDBusObjectPathCacheReference(const std::string& dbusObjectPath,
+ const DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ if (!dbusDaemonProxy_->isAvailable()) {
+ return;
}
- Subscription listenerSubscription = dbusServiceListenerList.insert(
- dbusServiceListenerList.end(), serviceListener);
+ if (dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ return;
+ }
- switch (dbusConnectionNameState) {
- case DBusServiceState::AVAILABLE:
- resolveDBusServiceInstances(dbusServiceIterator);
- break;
+ assert(!dbusServiceListenersRecord.uniqueBusName.empty());
- case DBusServiceState::RESOLVING:
- if (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE) {
- const SubscriptionStatus status = serviceListener(dbusInstanceAvailabilityStatus);
- if (status == SubscriptionStatus::CANCEL) {
- dbusServiceListenerList.erase(listenerSubscription);
- return dbusServiceListenerList.end();
- }
- }
- break;
-
- case DBusServiceState::RESOLVED:
- case DBusServiceState::NOT_AVAILABLE:
- const SubscriptionStatus status = serviceListener(dbusInstanceAvailabilityStatus);
- if (status == SubscriptionStatus::CANCEL) {
- dbusServiceListenerList.erase(listenerSubscription);
- return dbusServiceListenerList.end();
- }
- break;
+ auto& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceListenersRecord.uniqueBusName];
+ assert(!dbusUniqueNameRecord.ownedBusNames.empty());
+ assert(!dbusUniqueNameRecord.dbusObjectPathsCache.empty());
+
+ auto dbusObjectPathCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ const bool isDBusObjectPathCacheFound = (dbusObjectPathCacheIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end());
+ assert(isDBusObjectPathCacheFound);
+
+ auto& dbusObjectPathCache = dbusObjectPathCacheIterator->second;
+ assert(dbusObjectPathCache.referenceCount > 0);
+
+ dbusObjectPathCache.referenceCount--;
+
+ if (dbusObjectPathCache.referenceCount == 0) {
+ dbusUniqueNameRecord.dbusObjectPathsCache.erase(dbusObjectPathCacheIterator);
+
+ const bool isLastDBusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache.empty();
+ if (isLastDBusObjectPathCache) {
+ auto dbusProxyConnection = dbusDaemonProxy_->getDBusConnection();
+ const bool isSubscriptionCancelled = dbusProxyConnection->removeObjectManagerSignalMemberHandler(
+ dbusServiceListenersRecord.uniqueBusName,
+ this);
+ assert(isSubscriptionCancelled);
+ }
}
- notificationThread_ = std::thread::id();
- return listenerSubscription;
}
-void DBusServiceRegistry::unsubscribeAvailabilityListener(const std::string& commonApiAddress,
- Subscription& listenerSubscription) {
- std::string dbusInterfaceName;
- std::string dbusServiceName;
- std::string dbusObjectPath;
+
+bool DBusServiceRegistry::introspectDBusObjectPath(const std::string& dbusServiceUniqueName,
+ const std::string& dbusObjectPath) {
+ bool isResolvingInProgress = false;
+ auto dbusConnection = dbusDaemonProxy_->getDBusConnection();
+
+ assert(!dbusServiceUniqueName.empty());
+
+ if (dbusConnection->isConnected()) {
+ mutexObjectPathsResolveCount.lock();
+ objectPathsToResolve++;
+ mutexObjectPathsResolveCount.unlock();
+
+ DBusMessage dbusMessageCall = DBusMessage::createMethodCall(
+ dbusServiceUniqueName,
+ dbusObjectPath,
+ "org.freedesktop.DBus.Introspectable",
+ "Introspect");
+ auto instrospectAsyncCallback = std::bind(
+ &DBusServiceRegistry::onIntrospectCallback,
+ this->shared_from_this(),
+ std::placeholders::_1,
+ std::placeholders::_2,
+ dbusServiceUniqueName,
+ dbusObjectPath);
+
+ dbusConnection->sendDBusMessageWithReplyAsync(
+ dbusMessageCall,
+ DBusProxyAsyncCallbackHandler<std::string>::create(instrospectAsyncCallback),
+ 2000);
+
+ isResolvingInProgress = true;
+ }
+
+ return isResolvingInProgress;
+}
+
+/**
+ * Callback for org.freedesktop.DBus.Introspectable.Introspect
+ *
+ * This is the other end of checking if a dbus object path is available.
+ * On success it'll extract all interface names from the xml data response.
+ * Special interfaces that start with org.freedesktop will be ignored.
+ *
+ * @param status
+ * @param xmlData
+ * @param dbusServiceUniqueName
+ * @param dbusObjectPath
+ */
+void DBusServiceRegistry::onIntrospectCallback(const CallStatus& callStatus,
+ std::string xmlData,
+ const std::string& dbusServiceUniqueName,
+ const std::string& dbusObjectPath) {
+ if (callStatus == CallStatus::SUCCESS) {
+ parseIntrospectionData(xmlData, dbusObjectPath, dbusServiceUniqueName);
+ }
std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, dbusInterfaceName, dbusServiceName, dbusObjectPath);
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
+ // Error CallStatus will result in empty parsedDBusInterfaceNameSet (and not available notification)
- if (dbusServiceIterator == dbusServices_.end()) {
+ auto dbusServiceUniqueNameIterator = dbusUniqueNamesMap_.find(dbusServiceUniqueName);
+ const bool isDBusServiceUniqueNameFound = (dbusServiceUniqueNameIterator != dbusUniqueNamesMap_.end());
+
+ if (!isDBusServiceUniqueNameFound) {
return;
}
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ auto& dbusUniqueNameRecord = dbusServiceUniqueNameIterator->second;
+ auto dbusObjectPathIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ const bool isDBusObjectPathFound = (dbusObjectPathIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end());
- auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
- if (dbusInstanceIterator == dbusInstanceList.end()) {
+ if (!isDBusObjectPathFound) {
return;
}
- const AvailabilityStatus& dbusServiceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ auto& dbusObjectPathRecord = dbusObjectPathIterator->second;
+
+ dbusObjectPathRecord.state = DBusRecordState::RESOLVED;
+ dbusObjectPathRecord.promiseOnResolve.set_value(dbusObjectPathRecord.state);
+ mutexObjectPathsResolveCount.lock();
+ objectPathsToResolve++;
+ mutexObjectPathsResolveCount.unlock();
+ monitorResolveAllObjectPaths_.notify_all();
+
+ dbusUniqueNameRecord.objectPathsState = DBusRecordState::RESOLVED;
+
+ notifyDBusServiceListeners(
+ dbusUniqueNameRecord,
+ dbusObjectPath,
+ dbusObjectPathRecord.dbusInterfaceNamesCache,
+ DBusRecordState::RESOLVED);
+}
+
+void DBusServiceRegistry::parseIntrospectionNode(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& fullObjectPath, const std::string& dbusServiceUniqueName) {
+ std::string nodeName;
- dbusServiceListenerList.erase(listenerSubscription);
+ for(pugi::xml_node& subNode : node.children()) {
+ nodeName = std::string(subNode.name());
- if (dbusServiceListenerList.empty() && dbusServiceAvailabilityStatus != AvailabilityStatus::AVAILABLE) {
- dbusInstanceList.erase(dbusInstanceIterator);
+ if(nodeName == "node") {
+ processIntrospectionObjectPath(subNode, rootObjectPath, dbusServiceUniqueName);
+ }
- if (dbusInstanceList.empty() && dbusServiceState == DBusServiceState::UNKNOWN) {
- dbusServices_.erase(dbusServiceIterator);
+ if(nodeName == "interface") {
+ processIntrospectionInterface(subNode, rootObjectPath, fullObjectPath, dbusServiceUniqueName);
}
}
}
-SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus) {
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+void DBusServiceRegistry::processIntrospectionObjectPath(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& dbusServiceUniqueName) {
+ std::string fullObjectPath = rootObjectPath;
- switch (availabilityStatus) {
- case AvailabilityStatus::AVAILABLE:
- dbusNameListStatus_ = AvailabilityStatus::UNKNOWN;
- dbusDaemonProxy_->listNamesAsync(std::bind(
- &DBusServiceRegistry::onListNamesCallback,
- this->shared_from_this(),
- std::placeholders::_1,
- std::placeholders::_2));
- break;
+ if(fullObjectPath.at(fullObjectPath.length()-1) != '/') {
+ fullObjectPath += "/";
+ }
- case AvailabilityStatus::NOT_AVAILABLE:
- auto dbusServiceIterator = dbusServices_.begin();
+ fullObjectPath += std::string(node.attribute("name").as_string());
- while (dbusServiceIterator != dbusServices_.end()) {
- dbusServiceIterator = onDBusServiceOffline(dbusServiceIterator, DBusServiceState::NOT_AVAILABLE);
- }
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceUniqueName];
+ DBusObjectPathCache& dbusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache[fullObjectPath];
+
+ if(dbusObjectPathCache.state == DBusRecordState::UNKNOWN) {
+ dbusObjectPathCache.state = DBusRecordState::RESOLVING;
+ introspectDBusObjectPath(dbusServiceUniqueName, fullObjectPath);
+ }
+
+ for(pugi::xml_node subNode : node.children()) {
+ parseIntrospectionNode(subNode, fullObjectPath, fullObjectPath, dbusServiceUniqueName);
+ }
+}
+
+void DBusServiceRegistry::processIntrospectionInterface(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& fullObjectPath, const std::string& dbusServiceUniqueName) {
+ std::string interfaceName = node.attribute("name").as_string();
- dbusNameListStatus_ = AvailabilityStatus::NOT_AVAILABLE;
- break;
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceUniqueName];
+ DBusObjectPathCache& dbusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache[fullObjectPath];
+
+ if(!isOrgFreedesktopDBusInterface(interfaceName)) {
+ dbusObjectPathCache.dbusInterfaceNamesCache.insert(interfaceName);
+ }
+
+ for(pugi::xml_node subNode : node.children()) {
+ parseIntrospectionNode(subNode, rootObjectPath, fullObjectPath, dbusServiceUniqueName);
+ }
+}
+
+void DBusServiceRegistry::parseIntrospectionData(const std::string& xmlData,
+ const std::string& rootObjectPath,
+ const std::string& dbusServiceUniqueName) {
+ pugi::xml_document xmlDocument;
+ pugi::xml_parse_result parsedResult = xmlDocument.load_buffer(xmlData.c_str(), xmlData.length(), pugi::parse_minimal, pugi::encoding_utf8);
+
+ if(parsedResult.status != pugi::xml_parse_status::status_ok) {
+ return;
+ }
+
+ const pugi::xml_node rootNode = xmlDocument.child("node");
+
+ dbusServicesMutex_.lock();
+
+ parseIntrospectionNode(rootNode, rootObjectPath, rootObjectPath, dbusServiceUniqueName);
+
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceUniqueName];
+ dbusServicesMutex_.unlock();
+}
+
+
+SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus) {
+ assert(availabilityStatus != AvailabilityStatus::UNKNOWN);
+
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+
+ for (auto& dbusServiceListenersIterator : dbusServiceListenersMap) {
+ const auto& dbusServiceName = dbusServiceListenersIterator.first;
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator.second;
+
+ if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
+ resolveDBusServiceName(dbusServiceName, dbusServiceListenersRecord);
+ } else {
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
+ }
}
return SubscriptionStatus::RETAIN;
@@ -407,240 +900,294 @@ SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const Avail
SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent(const std::string& affectedName,
const std::string& oldOwner,
const std::string& newOwner) {
- if (isDBusServiceName(affectedName)) {
- AvailabilityStatus dbusServiceAvailabilityStatus = AvailabilityStatus::AVAILABLE;
+ if (!isDBusServiceName(affectedName)) {
+ return SubscriptionStatus::RETAIN;
+ }
- if (newOwner.empty()) {
- dbusServiceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
- }
+ const bool isDBusServiceNameLost = newOwner.empty();
+ const std::string& dbusServiceUniqueName = (isDBusServiceNameLost ? oldOwner : newOwner);
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- notificationThread_ = std::this_thread::get_id();
- onDBusServiceAvailabilityStatus(affectedName, dbusServiceAvailabilityStatus);
- notificationThread_ = std::thread::id();
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+
+ if (isDBusServiceNameLost) {
+ auto dbusUniqueNameIterator = dbusUniqueNamesMap_.find(dbusServiceUniqueName);
+ const bool isDBusUniqueNameFound = (dbusUniqueNameIterator != dbusUniqueNamesMap_.end());
+
+ if (isDBusUniqueNameFound) {
+ auto& dbusServiceListenersRecord = dbusServiceListenersMap[affectedName];
+ //assert(dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED);
+ //assert(!dbusServiceListenersRecord.uniqueBusName.empty());
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
+ }
+ } else {
+ onDBusServiceAvailable(affectedName, dbusServiceUniqueName);
}
return SubscriptionStatus::RETAIN;
}
-void DBusServiceRegistry::onListNamesCallback(const CommonAPI::CallStatus& callStatus, std::vector<std::string> dbusNames) {
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- notificationThread_ = std::this_thread::get_id();
- if (callStatus == CallStatus::SUCCESS) {
- for (const std::string& dbusName : dbusNames) {
- if (isDBusServiceName(dbusName)) {
- onDBusServiceAvailabilityStatus(dbusName, AvailabilityStatus::AVAILABLE);
- }
- }
+
+void DBusServiceRegistry::onDBusServiceAvailable(const std::string& dbusServiceName,
+ const std::string& dbusServiceUniqueName) {
+ DBusUniqueNameRecord* dbusUniqueNameRecord = insertServiceNameMapping(dbusServiceUniqueName, dbusServiceName);
+
+ auto& dbusServiceListenersRecord = dbusServiceListenersMap[dbusServiceName];
+ const bool isDBusServiceNameObserved = !dbusServiceListenersRecord.dbusObjectPathListenersMap.empty();
+
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED) {
+ assert(dbusServiceListenersRecord.uniqueBusName == dbusServiceUniqueName);
+ return;
}
- dbusNameListStatus_ = AvailabilityStatus::AVAILABLE;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::RESOLVED;
+ dbusServiceListenersRecord.uniqueBusName = dbusServiceUniqueName;
- auto dbusServiceIterator = dbusServices_.begin();
- while (dbusServiceIterator != dbusServices_.end()) {
- const DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
+ if (!isDBusServiceNameObserved) {
+ return;
+ }
- if (dbusServiceState == DBusServiceState::UNKNOWN) {
- dbusServiceIterator = onDBusServiceOffline(dbusServiceIterator, DBusServiceState::NOT_AVAILABLE);
+ // resolve object path and notify service listners
+ for (auto dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.begin();
+ dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end();) {
+ const std::string& listenersDBusObjectPath = dbusObjectPathListenersIterator->first;
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
+ auto& dbusObjectPathRecord = getDBusObjectPathCacheReference(
+ listenersDBusObjectPath,
+ dbusServiceUniqueName,
+ *dbusUniqueNameRecord);
+
+ if (dbusObjectPathRecord.state == DBusRecordState::RESOLVED) {
+ notifyDBusObjectPathResolved(dbusInterfaceNameListenersMap, dbusObjectPathRecord.dbusInterfaceNamesCache);
+ }
+
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(
+ dbusObjectPathListenersIterator);
} else {
- dbusServiceIterator++;
+ dbusObjectPathListenersIterator++;
}
}
- notificationThread_ = std::thread::id();
}
+void DBusServiceRegistry::onDBusServiceNotAvailable(DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ const std::unordered_set<std::string> dbusInterfaceNamesCache;
-void DBusServiceRegistry::onDBusServiceAvailabilityStatus(const std::string& dbusServiceName, const AvailabilityStatus& availabilityStatus) {
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
+ auto dbusUniqueNameRecordIterator = dbusUniqueNamesMap_.find(dbusServiceListenersRecord.uniqueBusName);
- if (dbusServiceIterator != dbusServices_.end()) {
- onDBusServiceAvailabilityStatus(dbusServiceIterator, availabilityStatus);
- } else if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
- dbusServices_.insert({ dbusServiceName, { DBusServiceState::AVAILABLE, DBusInstanceList() } });
- dbusServiceChanged_.notify_all();
- }
-}
+ // fulfill all open promises on object path resolution
+ if(dbusUniqueNameRecordIterator != dbusUniqueNamesMap_.end()) {
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNameRecordIterator->second;
+ for (auto dbusObjectPathsCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.begin();
+ dbusObjectPathsCacheIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end();
+ dbusObjectPathsCacheIterator++) {
+ auto& dbusObjectPathsCache = dbusObjectPathsCacheIterator->second;
-DBusServiceRegistry::DBusServiceList::iterator DBusServiceRegistry::onDBusServiceAvailabilityStatus(DBusServiceList::iterator& dbusServiceIterator,
- const AvailabilityStatus& availabilityStatus) {
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ std::promise<DBusRecordState> promiseOnResolve = std::move(dbusObjectPathsCache.promiseOnResolve);
- if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
- const std::string& dbusServiceName = dbusServiceIterator->first;
+ try {
+ std::future<DBusRecordState> futureOnResolve = promiseOnResolve.get_future();
+ if(!futureOnResolve.valid()) {
+ promiseOnResolve.set_value(DBusRecordState::NOT_AVAILABLE);
+ }
+ } catch (std::future_error& e) { }
- if (dbusServiceState != DBusServiceState::RESOLVING) {
- resolveDBusServiceInstances(dbusServiceIterator);
}
-
- return dbusServiceIterator;
}
- dbusServiceState = (availabilityStatus == AvailabilityStatus::UNKNOWN) ?
- DBusServiceState::UNKNOWN :
- DBusServiceState::NOT_AVAILABLE;
+ removeUniqueName(dbusServiceListenersRecord.uniqueBusName);
- return onDBusServiceOffline(dbusServiceIterator, dbusServiceState);
-}
-
-DBusServiceRegistry::DBusServiceList::iterator DBusServiceRegistry::onDBusServiceOffline(DBusServiceList::iterator& dbusServiceIterator,
- const DBusServiceState& newDBusServiceState) {
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
- auto dbusInstanceIterator = dbusInstanceList.begin();
+ dbusServiceListenersRecord.uniqueBusName.clear();
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::NOT_AVAILABLE;
- assert(newDBusServiceState == DBusServiceState::UNKNOWN || newDBusServiceState == DBusServiceState::NOT_AVAILABLE);
- dbusServiceState = newDBusServiceState;
+ for (auto dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.begin();
+ dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end(); ) {
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
- while (dbusInstanceIterator != dbusInstanceList.end()) {
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ notifyDBusObjectPathResolved(dbusInterfaceNameListenersMap, dbusInterfaceNamesCache);
- // notify listeners
- if (!dbusServiceListenerList.empty()) {
- // the internal state is unknown until the next time we ask the object manager
- notifyDBusServiceListeners(dbusServiceListenerList, AvailabilityStatus::NOT_AVAILABLE);
- dbusInstanceAvailabilityStatus = AvailabilityStatus::UNKNOWN;
- dbusInstanceIterator++;
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(
+ dbusObjectPathListenersIterator);
} else {
- dbusInstanceIterator = dbusInstanceList.erase(dbusInstanceIterator);
+ dbusObjectPathListenersIterator++;
}
}
+}
- dbusServiceChanged_.notify_all();
+void DBusServiceRegistry::notifyDBusServiceListeners(const DBusUniqueNameRecord& dbusUniqueNameRecord,
+ const std::string& dbusObjectPath,
+ const std::unordered_set<std::string>& dbusInterfaceNames,
+ const DBusRecordState& dbusInterfaceNamesState) {
+ notificationThread_ = std::this_thread::get_id();
- if (dbusInstanceList.empty()) {
- return dbusServices_.erase(dbusServiceIterator);
- }
+ for (auto& dbusServiceName : dbusUniqueNameRecord.ownedBusNames) {
+ auto dbusServiceListenersIterator = dbusServiceListenersMap.find(dbusServiceName);
- dbusServiceIterator++;
+ if(dbusServiceListenersIterator == dbusServiceListenersMap.end()) {
+ continue;
+ }
- return dbusServiceIterator;
-}
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator->second;
+ if(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ continue;
+ }
-void DBusServiceRegistry::resolveDBusServiceInstances(DBusServiceList::iterator& dbusServiceIterator) {
- const std::string& dbusServiceName = dbusServiceIterator->first;
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
- std::vector<DBusServiceAddress> predefinedDBusServiceInstances;
+ auto dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.find(dbusObjectPath);
+ const bool isDBusObjectPathListenersRecordFound =
+ (dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end());
- dbusServiceState = DBusServiceState::RESOLVING;
+ if (!isDBusObjectPathListenersRecordFound) {
+ continue; // skip
+ }
- // add predefined instances
- DBusAddressTranslator::getInstance().getPredefinedInstances(dbusServiceName, predefinedDBusServiceInstances);
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
- for (auto& dbusServiceAddress : predefinedDBusServiceInstances) {
- const std::string& dbusObjectPath = std::get<1>(dbusServiceAddress);
- const std::string& dbusInterfaceName = std::get<2>(dbusServiceAddress);
+ if (dbusInterfaceNamesState == DBusRecordState::RESOLVED) {
+ notifyDBusObjectPathResolved(dbusInterfaceNameListenersMap, dbusInterfaceNames);
+ } else {
+ notifyDBusObjectPathChanged(dbusInterfaceNameListenersMap, dbusInterfaceNames, dbusInterfaceNamesState);
+ }
- onDBusServiceInstanceAvailable(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(dbusObjectPathListenersIterator);
+ }
}
- dbusServiceChanged_.notify_all();
-
- // search for remote instances
- DBusDaemonProxy::GetManagedObjectsAsyncCallback callback = std::bind(&DBusServiceRegistry::onGetManagedObjectsCallback,
- this->shared_from_this(),
- std::placeholders::_1,
- std::placeholders::_2,
- dbusServiceName);
- dbusDaemonProxy_->getManagedObjectsAsync(dbusServiceName, callback);
+ notificationThread_ = std::thread::id();
}
+void DBusServiceRegistry::notifyDBusObjectPathResolved(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap,
+ const std::unordered_set<std::string>& dbusInterfaceNames) {
+ for (auto dbusObjectPathListenersIterator = dbusInterfaceNameListenersMap.begin();
+ dbusObjectPathListenersIterator != dbusInterfaceNameListenersMap.end();) {
+ const auto& listenersDBusInterfaceName = dbusObjectPathListenersIterator->first;
+ auto& dbusInterfaceNameListenersRecord = dbusObjectPathListenersIterator->second;
-void DBusServiceRegistry::onGetManagedObjectsCallback(const CallStatus& callStatus,
- DBusDaemonProxy::DBusObjectToInterfaceDict managedObjects,
- const std::string& dbusServiceName) {
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ const auto& dbusInterfaceNameIterator = dbusInterfaceNames.find(listenersDBusInterfaceName);
+ const bool isDBusInterfaceNameAvailable = (dbusInterfaceNameIterator != dbusInterfaceNames.end());
- // already offline
- if (dbusNameListStatus_ == AvailabilityStatus::NOT_AVAILABLE) {
- return;
- }
+ notifyDBusInterfaceNameListeners(dbusInterfaceNameListenersRecord, isDBusInterfaceNameAvailable);
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
- if (dbusServiceIterator == dbusServices_.end()) {
- return;
+ if (dbusInterfaceNameListenersRecord.listenerList.empty()) {
+ dbusObjectPathListenersIterator = dbusInterfaceNameListenersMap.erase(dbusObjectPathListenersIterator);
+ } else {
+ dbusObjectPathListenersIterator++;
+ }
}
+}
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+void DBusServiceRegistry::notifyDBusObjectPathChanged(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap,
+ const std::unordered_set<std::string>& dbusInterfaceNames,
+ const DBusRecordState& dbusInterfaceNamesState) {
+ const bool isDBusInterfaceNameAvailable = (dbusInterfaceNamesState == DBusRecordState::AVAILABLE);
- dbusServiceState = DBusServiceState::RESOLVED;
+ assert(
+ dbusInterfaceNamesState == DBusRecordState::AVAILABLE
+ || dbusInterfaceNamesState == DBusRecordState::NOT_AVAILABLE);
- if (callStatus == CallStatus::SUCCESS) {
- for (auto& dbusObjectPathIterator : managedObjects) {
- const std::string& dbusObjectPath = dbusObjectPathIterator.first;
+ for (const auto& dbusInterfaceName : dbusInterfaceNames) {
+ auto dbusInterfaceNameListenersIterator = dbusInterfaceNameListenersMap.find(dbusInterfaceName);
+ const bool isDBusInterfaceNameObserved = (dbusInterfaceNameListenersIterator
+ != dbusInterfaceNameListenersMap.end());
- for (auto& dbusInterfaceNameIterator : dbusObjectPathIterator.second) {
- const std::string& dbusInterfaceName = dbusInterfaceNameIterator.first;
+ if (isDBusInterfaceNameObserved) {
+ auto& dbusInterfaceNameListenersRecord = dbusInterfaceNameListenersIterator->second;
- onDBusServiceInstanceAvailable(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
- }
+ notifyDBusInterfaceNameListeners(dbusInterfaceNameListenersRecord, isDBusInterfaceNameAvailable);
}
}
+}
- dbusServiceChanged_.notify_all();
+void DBusServiceRegistry::notifyDBusInterfaceNameListeners(DBusInterfaceNameListenersRecord& dbusInterfaceNameListenersRecord,
+ const bool& isDBusInterfaceNameAvailable) {
+ // FIXME maybe store simple boolean into the DBusInterfaceNameListenersRecord (only 2 states are allowed)
+ const AvailabilityStatus availabilityStatus = (isDBusInterfaceNameAvailable ?
+ AvailabilityStatus::AVAILABLE : AvailabilityStatus::NOT_AVAILABLE);
+ const DBusRecordState notifyState = (isDBusInterfaceNameAvailable ?
+ DBusRecordState::AVAILABLE : DBusRecordState::NOT_AVAILABLE);
- // notify only UNKNOWN. The predefined and resolved have already been handled
- for (auto& dbusInstanceIterator : dbusInstanceList) {
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator.second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator.second.second;
+ if (notifyState == dbusInterfaceNameListenersRecord.state) {
+ return;
+ }
+ dbusInterfaceNameListenersRecord.state = notifyState;
+
+ for (auto dbusServiceListenerIterator = dbusInterfaceNameListenersRecord.listenerList.begin();
+ dbusServiceListenerIterator != dbusInterfaceNameListenersRecord.listenerList.end();) {
+ const auto& dbusServiceListener = *dbusServiceListenerIterator;
- if (dbusInstanceAvailabilityStatus == AvailabilityStatus::UNKNOWN) {
- dbusInstanceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
- notifyDBusServiceListeners(dbusServiceListenerList, dbusInstanceAvailabilityStatus);
+ if (dbusServiceListener(availabilityStatus) != SubscriptionStatus::RETAIN) {
+ dbusServiceListenerIterator = dbusInterfaceNameListenersRecord.listenerList.erase(
+ dbusServiceListenerIterator);
+ } else {
+ dbusServiceListenerIterator++;
}
}
}
+void DBusServiceRegistry::removeUniqueName(std::string& dbusUniqueName) {
+ if(dbusUniqueName.empty()) {
+ return;
+ }
-void DBusServiceRegistry::onDBusServiceInstanceAvailable(DBusInstanceList& dbusInstanceList,
- const std::string& dbusObjectPath,
- const std::string& dbusInterfaceName) {
- auto dbusInstanceIterator = addDBusServiceInstance(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ auto dbusUniqueNamesIterator = dbusUniqueNamesMap_.find(dbusUniqueName);
- dbusInstanceAvailabilityStatus = AvailabilityStatus::AVAILABLE;
+ if(dbusUniqueNamesIterator != dbusUniqueNamesMap_.end()) {
+ for (auto dbusServiceNamesIterator = dbusUniqueNamesIterator->second.ownedBusNames.begin();
+ dbusServiceNamesIterator != dbusUniqueNamesIterator->second.ownedBusNames.end();
+ dbusServiceNamesIterator++) {
+ dbusServiceNameMap_.erase(*dbusServiceNamesIterator);
+ }
- notifyDBusServiceListeners(dbusServiceListenerList, dbusInstanceAvailabilityStatus);
+ dbusUniqueNamesMap_.erase(dbusUniqueNamesIterator);
+ }
}
+DBusServiceRegistry::DBusUniqueNameRecord* DBusServiceRegistry::insertServiceNameMapping(const std::string& dbusUniqueName,
+ const std::string& dbusServiceName) {
+ auto* dbusUniqueNameRecord = &(dbusUniqueNamesMap_[dbusUniqueName]);
+ dbusUniqueNameRecord->uniqueName = dbusUniqueName;
+ dbusUniqueNameRecord->ownedBusNames.insert(dbusServiceName);
-DBusServiceRegistry::DBusInstanceList::iterator DBusServiceRegistry::addDBusServiceInstance(DBusInstanceList& dbusInstanceList,
- const std::string& dbusObjectPath,
- const std::string& dbusInterfaceName) {
- auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
+ auto dbusServiceNameMapIterator = dbusServiceNameMap_.find(dbusServiceName);
- // add instance for the first time
- if (dbusInstanceIterator == dbusInstanceList.end()) {
- auto insertIterator = dbusInstanceList.insert(
- { { dbusObjectPath, dbusInterfaceName }, { AvailabilityStatus::UNKNOWN, DBusServiceListenerList() } });
- const bool& insertSuccessfull = insertIterator.second;
+ if(dbusServiceNameMapIterator == dbusServiceNameMap_.end()) {
+ dbusServiceNameMap_.insert({ dbusServiceName, dbusUniqueNameRecord });
+ }
+ else {
+ dbusServiceNameMapIterator->second = dbusUniqueNameRecord;
+ }
- assert(insertSuccessfull);
- dbusInstanceIterator = insertIterator.first;
+ DBusServiceListenersRecord& dbusServiceListenersRecord = dbusServiceListenersMap[dbusServiceName];
+
+ if(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ dbusServiceListenersRecord.uniqueBusName = dbusUniqueName;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::UNKNOWN;
}
- return dbusInstanceIterator;
+ return dbusUniqueNameRecord;
}
-void DBusServiceRegistry::notifyDBusServiceListeners(DBusServiceListenerList& dbusServiceListenerList,
- const AvailabilityStatus& availabilityStatus) {
- for (auto dbusServiceListenerIterator = dbusServiceListenerList.begin(); dbusServiceListenerIterator != dbusServiceListenerList.end(); ++dbusServiceListenerIterator) {
- const SubscriptionStatus status = (*dbusServiceListenerIterator)(availabilityStatus);
- if (status == SubscriptionStatus::CANCEL) {
- dbusServiceListenerIterator = dbusServiceListenerList.erase(dbusServiceListenerIterator);
- }
+/**
+ * finds a DBusUniquNameRecord associated with a given well-known name.
+ * The returned DBusUniquNameRecord* may be a NULL pointer, if the well-known
+ * name is known, but not associated with a unique name yet.
+ *
+ * @return true, if the given well-known name is found
+ */
+bool DBusServiceRegistry::findCachedDbusService(
+ const std::string& dbusServiceName,
+ DBusUniqueNameRecord** uniqueNameRecord) {
+ auto dbusUniqueNameRecordIterator = dbusServiceNameMap_.find(dbusServiceName);
+
+ if(dbusUniqueNameRecordIterator != dbusServiceNameMap_.end()) {
+ *uniqueNameRecord = dbusUniqueNameRecordIterator->second;
+ return true;
}
-}
-bool DBusServiceRegistry::isDBusServiceName(const std::string& name) {
- return name[0] != ':';
+ return false;
}
-}// namespace DBus
-}// namespace CommonAPI
+} // namespace DBus
+} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusServiceRegistry.h b/src/CommonAPI/DBus/DBusServiceRegistry.h
index d7426bf..c433d82 100644
--- a/src/CommonAPI/DBus/DBusServiceRegistry.h
+++ b/src/CommonAPI/DBus/DBusServiceRegistry.h
@@ -21,7 +21,10 @@
#include "DBusAddressTranslator.h"
#include "DBusDaemonProxy.h"
+#include "pugixml/pugixml.hpp"
+
#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <map>
#include <unordered_set>
@@ -34,6 +37,7 @@
#include <condition_variable>
#include <mutex>
+#include <future>
namespace CommonAPI {
namespace DBus {
@@ -47,10 +51,10 @@ typedef std::pair<std::string, std::string> DBusInstanceId;
class DBusProxyConnection;
class DBusDaemonProxy;
-
-class DBusServiceRegistry: public std::enable_shared_from_this<DBusServiceRegistry> {
+class DBusServiceRegistry: public std::enable_shared_from_this<DBusServiceRegistry>,
+ public DBusProxyConnection::DBusSignalHandler {
public:
- enum class DBusServiceState {
+ enum class DBusRecordState {
UNKNOWN,
AVAILABLE,
RESOLVING,
@@ -58,27 +62,37 @@ class DBusServiceRegistry: public std::enable_shared_from_this<DBusServiceRegist
NOT_AVAILABLE
};
+ // template class DBusServiceListener<> { typedef functor; typedef list; typedef subscription }
typedef std::function<SubscriptionStatus(const AvailabilityStatus& availabilityStatus)> DBusServiceListener;
typedef std::list<DBusServiceListener> DBusServiceListenerList;
- typedef DBusServiceListenerList::iterator Subscription;
+ typedef DBusServiceListenerList::iterator DBusServiceSubscription;
+
- typedef std::pair<std::string, std::string> DBusObjectInterfacePair;
- typedef std::unordered_map<DBusObjectInterfacePair, std::pair<AvailabilityStatus, DBusServiceListenerList> > DBusInstanceList;
- typedef std::unordered_map<std::string, std::pair<DBusServiceState, DBusInstanceList> > DBusServiceList;
+ typedef std::function<SubscriptionStatus(const std::vector<std::string>& interfaces,
+ const AvailabilityStatus& availabilityStatus)> DBusManagedInterfaceListener;
+ typedef std::list<DBusManagedInterfaceListener> DBusManagedInterfaceListenerList;
+ typedef DBusManagedInterfaceListenerList::iterator DBusManagedInterfaceSubscription;
DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection);
+ DBusServiceRegistry(const DBusServiceRegistry&) = delete;
+ DBusServiceRegistry& operator=(const DBusServiceRegistry&) = delete;
+
virtual ~DBusServiceRegistry();
void init();
- bool isServiceInstanceAlive(const std::string& dbusInterfaceName, const std::string& dbusConnectionName, const std::string& dbusObjectPath);
- Subscription subscribeAvailabilityListener(const std::string& commonApiAddress,
- DBusServiceListener serviceListener);
+ DBusServiceSubscription subscribeAvailabilityListener(const std::string& commonApiAddress,
+ DBusServiceListener serviceListener);
+
void unsubscribeAvailabilityListener(const std::string& commonApiAddress,
- Subscription& listenerSubscription);
+ DBusServiceSubscription& listenerSubscription);
+
+
+ bool isServiceInstanceAlive(const std::string& dbusInterfaceName, const std::string& dbusConnectionName, const std::string& dbusObjectPath);
+
virtual std::vector<std::string> getAvailableServiceInstances(const std::string& interfaceName,
const std::string& domainName = "local");
@@ -87,57 +101,223 @@ class DBusServiceRegistry: public std::enable_shared_from_this<DBusServiceRegist
const std::string& interfaceName,
const std::string& domainName = "local");
- private:
- DBusServiceRegistry(const DBusServiceRegistry&) = delete;
- DBusServiceRegistry& operator=(const DBusServiceRegistry&) = delete;
- SubscriptionStatus onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus);
- SubscriptionStatus onDBusDaemonProxyNameOwnerChangedEvent(const std::string& name, const std::string& oldOwner, const std::string& newOwner);
+ virtual SubscriptionStatus onSignalDBusMessage(const DBusMessage&);
+
+// std::vector<std::string> getManagedObjects(const std::string& connectionName, const std::string& objectpath);
- void onListNamesCallback(const CommonAPI::CallStatus& callStatus, std::vector<std::string> dbusNames);
+ private:
+ struct DBusInterfaceNameListenersRecord {
+ DBusInterfaceNameListenersRecord(): state(DBusRecordState::UNKNOWN) {
+ }
+
+ DBusInterfaceNameListenersRecord(DBusInterfaceNameListenersRecord&& other):
+ state(other.state),
+ listenerList(std::move(other.listenerList))
+ {
+ }
+
+ DBusRecordState state;
+ DBusServiceListenerList listenerList;
+ };
- void resolveDBusServiceInstances(DBusServiceList::iterator& dbusServiceIterator);
- void onGetManagedObjectsCallback(const CallStatus& status, DBusDaemonProxy::DBusObjectToInterfaceDict managedObjects, const std::string& dbusServiceName);
+ typedef std::unordered_map<std::string, DBusInterfaceNameListenersRecord> DBusInterfaceNameListenersMap;
+
+ struct DBusServiceListenersRecord {
+ DBusServiceListenersRecord(): uniqueBusNameState(DBusRecordState::UNKNOWN),
+ //futureOnResolve(),
+ mutexOnResolve() {
+ }
+
+ DBusServiceListenersRecord(DBusServiceListenersRecord&& other):
+ uniqueBusName(std::move(other.uniqueBusName)),
+ uniqueBusNameState(other.uniqueBusNameState),
+ promiseOnResolve(std::move(other.promiseOnResolve)),
+ futureOnResolve(std::move(other.futureOnResolve)),
+ mutexOnResolve(std::move(other.mutexOnResolve)),
+ dbusObjectPathListenersMap(std::move(other.dbusObjectPathListenersMap))
+ {
+ /*other.uniqueBusName = NULL;
+ other.promiseOnResolve = NULL;
+ other.futureOnResolve = NULL;
+ other.mutexOnResolve = NULL;
+ other.dbusObjectPathListenersMap = NULL;
+ other.dbusServiceListenersMap = NULL;*/
+ }
+
+ ~DBusServiceListenersRecord() {};
+
+ DBusRecordState uniqueBusNameState;
+ std::string uniqueBusName;
+
+ std::promise<DBusRecordState> promiseOnResolve;
+ std::shared_future<DBusRecordState> futureOnResolve;
+ std::unique_lock<std::mutex>* mutexOnResolve;
+
+ std::unordered_map<std::string, DBusInterfaceNameListenersMap> dbusObjectPathListenersMap;
+
+ /* per manager records: anser commonapi queries and update after dbus GetManagedObjects */
+// typedef std::unordered_set<std::string> DBusObjectPathsSet;
+// typedef std::pair<DBusObjectPathsSet, DBusManagedInterfaceListenerList> DBusManagedInterfaceRecord;
+// typedef std::unordered_map<std::string, DBusManagedInterfaceRecord> DBusManagedInterfacesMap;
+// typedef std::pair<DBusRecordState, DBusManagedInterfacesMap> DBusObjectManagerRecord;
+// std::unordered_map<std::string, DBusObjectManagerRecord> dbusObjectManagersMap;
+ };
- size_t getResolvedServiceInstances(const std::string& dbusInterfaceName, std::vector<std::string>& availableServiceInstances);
- size_t getNumResolvingServiceInstances();
+ std::unordered_map<std::string, DBusServiceListenersRecord> dbusServiceListenersMap;
- bool waitDBusServicesAvailable(std::unique_lock<std::mutex>& lock, std::chrono::milliseconds& timeout);
- void onDBusServiceAvailabilityStatus(const std::string& dbusServiceName, const AvailabilityStatus& availabilityStatus);
- DBusServiceList::iterator onDBusServiceAvailabilityStatus(DBusServiceList::iterator& dbusServiceIterator, const AvailabilityStatus& availabilityStatus);
- DBusServiceList::iterator onDBusServiceOffline(DBusServiceList::iterator& dbusServiceIterator, const DBusServiceState& dbusServiceState);
+ struct DBusObjectPathCache {
+ DBusObjectPathCache(): referenceCount(0), state(DBusRecordState::UNKNOWN) {
+ }
- static void onDBusServiceInstanceAvailable(
- DBusInstanceList& dbusInstanceList,
- const std::string& dbusObjectPath,
- const std::string& dbusInterfaceName);
+ DBusObjectPathCache(DBusObjectPathCache&& other):
+ referenceCount(other.referenceCount),
+ state(other.state),
+ promiseOnResolve(std::move(other.promiseOnResolve)),
+ dbusInterfaceNamesCache(std::move(other.dbusInterfaceNamesCache))
+ {
+ /*other.promiseOnResolve = NULL;
+ other.dbusInterfaceNamesCache = NULL;*/
+ }
- static DBusInstanceList::iterator addDBusServiceInstance(
- DBusInstanceList& dbusInstanceList,
- const std::string& dbusObjectPath,
- const std::string& dbusInterfaceName);
+ ~DBusObjectPathCache() {}
- static void notifyDBusServiceListeners(DBusServiceListenerList& dbusServiceListenerList, const AvailabilityStatus& availabilityStatus);
+ size_t referenceCount;
+ DBusRecordState state;
+ std::promise<DBusRecordState> promiseOnResolve;
- static bool isDBusServiceName(const std::string& name);
+ std::unordered_set<std::string> dbusInterfaceNamesCache;
+ };
+ struct DBusUniqueNameRecord {
+ DBusUniqueNameRecord(): objectPathsState(DBusRecordState::UNKNOWN) {
+ }
+
+ DBusUniqueNameRecord(DBusUniqueNameRecord&& other) :
+ uniqueName(std::move(other.uniqueName)),
+ objectPathsState(other.objectPathsState),
+ ownedBusNames(std::move(other.ownedBusNames)),
+ dbusObjectPathsCache(std::move(other.dbusObjectPathsCache))
+ {
+ /*other.uniqueName = NULL;
+ other.ownedBusNames = NULL;
+ other.dbusObjectPathsCache = NULL;*/
+ }
+ std::string uniqueName;
+ DBusRecordState objectPathsState;
+ std::unordered_set<std::string> ownedBusNames;
+ std::unordered_map<std::string, DBusObjectPathCache> dbusObjectPathsCache;
+
+ // TODO managed objects
+ };
- std::shared_ptr<DBusDaemonProxy> dbusDaemonProxy_;
+ std::unordered_map<std::string, DBusUniqueNameRecord> dbusUniqueNamesMap_;
+ // mapping service names (well-known names) to service instances
+ std::unordered_map<std::string, DBusUniqueNameRecord*> dbusServiceNameMap_;
- DBusServiceList dbusServices_;
- AvailabilityStatus dbusNameListStatus_;
- std::condition_variable dbusServiceChanged_;
+ // protects the dbus service maps
std::mutex dbusServicesMutex_;
- std::thread::id notificationThread_;
+
+ void resolveDBusServiceName(const std::string& dbusServiceName,
+ DBusServiceListenersRecord& dbusServiceListenersRecord);
+
+ void onGetNameOwnerCallback(const CallStatus& status, std::string dbusServiceUniqueName, const std::string& dbusServiceName);
+
+
+ DBusRecordState resolveDBusInterfaceNameState(const std::string& dbusInterfaceName,
+ const std::string& dbusObjectPath,
+ const std::string& dbusServiceName,
+ DBusServiceListenersRecord& dbusServiceListenersRecord);
+
+
+ DBusObjectPathCache& getDBusObjectPathCacheReference(const std::string& dbusObjectPath,
+ const std::string& dbusServiceUniqueName,
+ DBusUniqueNameRecord& dbusUniqueNameRecord);
+
+ void releaseDBusObjectPathCacheReference(const std::string& dbusObjectPath,
+ const DBusServiceListenersRecord& dbusServiceListenersRecord);
+
+
+ bool introspectDBusObjectPath(const std::string& dbusServiceUniqueName, const std::string& dbusObjectPath);
+
+ void onIntrospectCallback(const CallStatus& status,
+ std::string xmlData,
+ const std::string& dbusServiceName,
+ const std::string& dbusObjectPath);
+
+ void parseIntrospectionData(const std::string& xmlData, const std::string& rootObjectPath, const std::string& dbusServiceUniqueName);
+ void parseIntrospectionNode(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& fullObjectPath, const std::string& dbusServiceUniqueName);
+ void processIntrospectionObjectPath(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& dbusServiceUniqueName);
+ void processIntrospectionInterface(const pugi::xml_node& interface, const std::string& rootObjectPath, const std::string& fullObjectPath, const std::string& dbusServiceUniqueName);
+
+
+
+ SubscriptionStatus onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus);
+
+ SubscriptionStatus onDBusDaemonProxyNameOwnerChangedEvent(const std::string& name,
+ const std::string& oldOwner,
+ const std::string& newOwner);
+
+ std::shared_ptr<DBusDaemonProxy> dbusDaemonProxy_;
bool initialized_;
ProxyStatusEvent::Subscription dbusDaemonProxyStatusEventSubscription_;
NameOwnerChangedEvent::Subscription dbusDaemonProxyNameOwnerChangedEventSubscription_;
+
+
+ void onDBusServiceAvailable(const std::string& dbusServiceName, const std::string& dbusServiceUniqueName);
+
+ void onDBusServiceNotAvailable(DBusServiceListenersRecord& dbusServiceListenersRecord);
+
+
+ void notifyDBusServiceListeners(const DBusUniqueNameRecord& dbusUniqueNameRecord,
+ const std::string& dbusObjectPath,
+ const std::unordered_set<std::string>& dbusInterfaceNames,
+ const DBusRecordState& dbusInterfaceNamesState);
+
+ void notifyDBusObjectPathResolved(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap,
+ const std::unordered_set<std::string>& dbusInterfaceNames);
+
+ void notifyDBusObjectPathChanged(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap,
+ const std::unordered_set<std::string>& dbusInterfaceNames,
+ const DBusRecordState& dbusInterfaceNamesState);
+
+ void notifyDBusInterfaceNameListeners(DBusInterfaceNameListenersRecord& dbusInterfaceNameListenersRecord,
+ const bool& isDBusInterfaceNameAvailable);
+
+
+ void removeUniqueName(std::string& dbusUniqueName);
+ DBusUniqueNameRecord* insertServiceNameMapping(const std::string& dbusUniqueName, const std::string& dbusServiceName);
+ bool findCachedDbusService(const std::string& dbusServiceName, DBusUniqueNameRecord** uniqueNameRecord);
+ bool findCachedObjectPath(const std::string& dbusObjectPathName, const DBusUniqueNameRecord* uniqueNameRecord, DBusObjectPathCache* objectPathCache);
+
+ std::condition_variable monitorResolveAllServices_;
+ std::mutex mutexServiceResolveCount;
+ int servicesToResolve;
+
+ std::condition_variable monitorResolveAllObjectPaths_;
+ std::mutex mutexObjectPathsResolveCount;
+ int objectPathsToResolve;
+
+
+ void fetchAllServiceNames();
+
+ inline const bool isDBusServiceName(const std::string& serviceName) {
+ return (serviceName.length() > 0 && serviceName[0] != ':');
+ };
+
+
+ inline const bool isOrgFreedesktopDBusInterface(const std::string& dbusInterfaceName) {
+ return dbusInterfaceName.find("org.freedesktop.") == 0;
+ }
+
+ std::thread::id notificationThread_;
};
+
} // namespace DBus
} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusServiceStatusEvent.cpp b/src/CommonAPI/DBus/DBusServiceStatusEvent.cpp
deleted file mode 100644
index c82dadc..0000000
--- a/src/CommonAPI/DBus/DBusServiceStatusEvent.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (C) 2013 BMW Group
- * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
- * Author: Juergen Gehring (juergen.gehring@bmw.de)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "DBusServiceStatusEvent.h"
-#include "DBusServiceRegistry.h"
-#include <cassert>
-
-namespace CommonAPI {
-namespace DBus {
-
-DBusServiceStatusEvent::DBusServiceStatusEvent(std::shared_ptr<DBusServiceRegistry> registry) :
- registry_(registry) {
-}
-
-void DBusServiceStatusEvent::onFirstListenerAdded(const std::string& commonApiServiceName, const Listener& listener) {
-
-}
-
-SubscriptionStatus DBusServiceStatusEvent::availabilityEvent(const std::string& commonApiServiceName, const AvailabilityStatus& availabilityStatus) {
- notifyListeners(commonApiServiceName, availabilityStatus);
- return SubscriptionStatus::RETAIN;
-}
-
-void DBusServiceStatusEvent::onListenerAdded(const std::string& commonApiServiceName, const Listener& listener) {
- if (registry_) {
- registry_->subscribeAvailabilityListener(commonApiServiceName, std::bind(
- &DBusServiceStatusEvent::availabilityEvent,
- this,
- commonApiServiceName,
- std::placeholders::_1));
-
- std::string dbusInterfaceName;
- std::string dbusConnectionName;
- std::string dbusObjectPath;
- DBusAddressTranslator::getInstance().searchForDBusAddress(
- commonApiServiceName,
- dbusInterfaceName,
- dbusConnectionName,
- dbusObjectPath);
-
- const AvailabilityStatus availabilityStatus =
- !registry_->isServiceInstanceAlive(dbusInterfaceName, dbusConnectionName, dbusObjectPath) ? AvailabilityStatus::NOT_AVAILABLE :
- AvailabilityStatus::AVAILABLE;
-
- notifyListeners(commonApiServiceName, availabilityStatus);
- }
-}
-
-} // namespace DBus
-} // namespace CommonAPI
diff --git a/src/CommonAPI/DBus/DBusServiceStatusEvent.h b/src/CommonAPI/DBus/DBusServiceStatusEvent.h
deleted file mode 100644
index 4e8956a..0000000
--- a/src/CommonAPI/DBus/DBusServiceStatusEvent.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright (C) 2013 BMW Group
- * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
- * Author: Juergen Gehring (juergen.gehring@bmw.de)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#if !defined (COMMONAPI_INTERNAL_COMPILATION)
-#error "Only <CommonAPI/CommonAPI.h> can be included directly, this file may disappear or change contents."
-#endif
-
-#ifndef COMMONAPI_DBUS_DBUS_SERVICE_STATUS_EVENT_H_
-#define COMMONAPI_DBUS_DBUS_SERVICE_STATUS_EVENT_H_
-
-#include "DBusMultiEvent.h"
-
-#include <CommonAPI/Event.h>
-#include <CommonAPI/types.h>
-
-#include <string>
-#include <memory>
-
-namespace CommonAPI {
-namespace DBus {
-
-
-class DBusServiceRegistry;
-
-class DBusServiceStatusEvent: public DBusMultiEvent<AvailabilityStatus> {
- public:
- DBusServiceStatusEvent(std::shared_ptr<DBusServiceRegistry> registry);
-
- protected:
- void onFirstListenerAdded(const std::string& commonApiServiceName, const Listener& listener);
- void onListenerAdded(const std::string& commonApiServiceName, const Listener& listener);
-
- private:
- SubscriptionStatus availabilityEvent(const std::string& commonApiServiceName, const AvailabilityStatus& availabilityStatus);
-
- std::shared_ptr<DBusServiceRegistry> registry_;
-};
-
-} // namespace DBus
-} // namespace CommonAPI
-
-#endif // COMMONAPI_DBUS_DBUS_SERVICE_STATUS_EVENT_H_
-
diff --git a/src/CommonAPI/DBus/DBusStubAdapter.cpp b/src/CommonAPI/DBus/DBusStubAdapter.cpp
index c71c47f..d0367b3 100644
--- a/src/CommonAPI/DBus/DBusStubAdapter.cpp
+++ b/src/CommonAPI/DBus/DBusStubAdapter.cpp
@@ -18,11 +18,14 @@ namespace DBus {
const std::string DBusStubAdapter::domain_ = "local";
-DBusStubAdapter::DBusStubAdapter(const std::string& commonApiAddress,
+DBusStubAdapter::DBusStubAdapter(const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
const std::string& dbusObjectPath,
- const std::shared_ptr<DBusProxyConnection>& dbusConnection) :
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection,
+ DBusObjectManagerStub* managerStub) :
+ factory_(factory),
commonApiDomain_(split(commonApiAddress, ':')[0]),
commonApiServiceId_(split(commonApiAddress, ':')[1]),
commonApiParticipantId_(split(commonApiAddress, ':')[2]),
@@ -30,7 +33,7 @@ DBusStubAdapter::DBusStubAdapter(const std::string& commonApiAddress,
dbusBusName_(dbusBusName),
dbusObjectPath_(dbusObjectPath),
dbusConnection_(dbusConnection),
- isInitialized_(false) {
+ managerStub(managerStub) {
assert(!dbusBusName_.empty());
assert(!dbusInterfaceName_.empty());
@@ -41,26 +44,13 @@ DBusStubAdapter::DBusStubAdapter(const std::string& commonApiAddress,
}
DBusStubAdapter::~DBusStubAdapter() {
- deinit();
-}
-
-void DBusStubAdapter::deinit() {
- assert(dbusConnection_);
-
- if (isInitialized_) {
- dbusConnection_->getDBusObjectManager()->unregisterDBusStubAdapter(dbusInterfaceHandlerToken_);
- dbusConnection_->releaseServiceName(dbusBusName_);
- isInitialized_ = false;
- }
+ deinit();
}
void DBusStubAdapter::init() {
- dbusInterfaceHandlerToken_ = dbusConnection_->getDBusObjectManager()->registerDBusStubAdapter(
- dbusObjectPath_,
- dbusInterfaceName_,
- this);
+}
- isInitialized_ = true;
+void DBusStubAdapter::deinit() {
}
const std::string DBusStubAdapter::getAddress() const {
diff --git a/src/CommonAPI/DBus/DBusStubAdapter.h b/src/CommonAPI/DBus/DBusStubAdapter.h
index a9646fd..7c365af 100644
--- a/src/CommonAPI/DBus/DBusStubAdapter.h
+++ b/src/CommonAPI/DBus/DBusStubAdapter.h
@@ -13,7 +13,7 @@
#define COMMONAPI_DBUS_DBUS_STUB_ADAPTER_H_
#include "DBusProxyConnection.h"
-#include "DBusObjectManager.h"
+#include "DBusInterfaceHandler.h"
#include "DBusMessage.h"
#include <CommonAPI/Stub.h>
@@ -24,13 +24,18 @@
namespace CommonAPI {
namespace DBus {
-class DBusStubAdapter: virtual public CommonAPI::StubAdapter {
+class DBusObjectManagerStub;
+class DBusFactory;
+
+class DBusStubAdapter: virtual public CommonAPI::StubAdapter, public DBusInterfaceHandler {
public:
- DBusStubAdapter(const std::string& commonApiAddress,
+ DBusStubAdapter(const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
const std::string& dbusObjectPath,
- const std::shared_ptr<DBusProxyConnection>& dbusConnection);
+ const std::shared_ptr<DBusProxyConnection>& dbusConnection,
+ DBusObjectManagerStub* managerStub = NULL);
virtual ~DBusStubAdapter();
@@ -42,14 +47,22 @@ class DBusStubAdapter: virtual public CommonAPI::StubAdapter {
virtual const std::string& getServiceId() const;
virtual const std::string& getInstanceId() const;
+ inline const std::string& getDBusName() const;
inline const std::string& getObjectPath() const;
inline const std::string& getInterfaceName() const;
inline const std::shared_ptr<DBusProxyConnection>& getDBusConnection() const;
+ inline virtual DBusObjectManagerStub* getDBusObjectManagerStub();
+ inline const bool hasDBusObjectManagerStub();
+
virtual const char* getMethodsDBusIntrospectionXmlData() const = 0;
virtual bool onInterfaceDBusMessage(const DBusMessage& dbusMessage) = 0;
-private:
+
+ virtual void deactivateManagedInstances() = 0;
+
+ protected:
+
const std::string commonApiDomain_;
const std::string commonApiServiceId_;
const std::string commonApiParticipantId_;
@@ -59,13 +72,26 @@ private:
const std::string dbusInterfaceName_;
const std::shared_ptr<DBusProxyConnection> dbusConnection_;
- bool isInitialized_;
-
- DBusInterfaceHandlerToken dbusInterfaceHandlerToken_;
+ DBusObjectManagerStub* managerStub;
static const std::string domain_;
+
+ const std::shared_ptr<DBusFactory> factory_;
};
+
+DBusObjectManagerStub* DBusStubAdapter::getDBusObjectManagerStub() {
+ return managerStub;
+}
+
+const bool DBusStubAdapter::hasDBusObjectManagerStub() {
+ return (getDBusObjectManagerStub() != NULL);
+}
+
+const std::string& DBusStubAdapter::getDBusName() const {
+ return dbusBusName_;
+}
+
const std::string& DBusStubAdapter::getObjectPath() const {
return dbusObjectPath_;
}
diff --git a/src/CommonAPI/DBus/DBusStubAdapterHelper.h b/src/CommonAPI/DBus/DBusStubAdapterHelper.h
index 24de3f1..9507214 100644
--- a/src/CommonAPI/DBus/DBusStubAdapterHelper.h
+++ b/src/CommonAPI/DBus/DBusStubAdapterHelper.h
@@ -40,13 +40,15 @@ class DBusStubAdapterHelper: public DBusStubAdapter, public std::enable_shared_f
};
public:
- DBusStubAdapterHelper(const std::string& commonApiAddress,
+ DBusStubAdapterHelper(const std::shared_ptr<DBusFactory>& factory,
+ const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
const std::string& dbusObjectPath,
const std::shared_ptr<DBusProxyConnection>& dbusConnection,
- const std::shared_ptr<_StubClass>& stub):
- DBusStubAdapter(commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, dbusConnection),
+ const std::shared_ptr<_StubClass>& stub,
+ DBusObjectManagerStub* managerStub = NULL):
+ DBusStubAdapter(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, dbusConnection, managerStub),
stub_(stub) {
}
diff --git a/src/pugixml/pugiconfig.hpp b/src/pugixml/pugiconfig.hpp
new file mode 100644
index 0000000..5a63fd4
--- /dev/null
+++ b/src/pugixml/pugiconfig.hpp
@@ -0,0 +1,69 @@
+/**
+ * pugixml parser - version 1.2
+ * --------------------------------------------------------
+ * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at http://pugixml.org/
+ *
+ * This library is distributed under the MIT License. See notice at the end
+ * of this file.
+ *
+ * This work is based on the pugxml parser, which is:
+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+ */
+
+#ifndef HEADER_PUGICONFIG_HPP
+#define HEADER_PUGICONFIG_HPP
+
+// Uncomment this to enable wchar_t mode
+// #define PUGIXML_WCHAR_MODE
+
+// Uncomment this to disable XPath
+// #define PUGIXML_NO_XPATH
+
+// Uncomment this to disable STL
+// #define PUGIXML_NO_STL
+
+// Uncomment this to disable exceptions
+// #define PUGIXML_NO_EXCEPTIONS
+
+// Set this to control attributes for public classes/functions, i.e.:
+// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
+// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
+// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
+// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
+
+// Uncomment this to switch to header-only version
+// #define PUGIXML_HEADER_ONLY
+// #include "pugixml.cpp"
+
+// Tune these constants to adjust memory-related behavior
+// #define PUGIXML_MEMORY_PAGE_SIZE 32768
+// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
+// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
+
+#endif
+
+/**
+ * Copyright (c) 2006-2012 Arseny Kapoulkine
+ *
+ * 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.
+ */
diff --git a/src/pugixml/pugixml.cpp b/src/pugixml/pugixml.cpp
new file mode 100644
index 0000000..4035ab1
--- /dev/null
+++ b/src/pugixml/pugixml.cpp
@@ -0,0 +1,10250 @@
+/**
+ * pugixml parser - version 1.2
+ * --------------------------------------------------------
+ * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at http://pugixml.org/
+ *
+ * This library is distributed under the MIT License. See notice at the end
+ * of this file.
+ *
+ * This work is based on the pugxml parser, which is:
+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+ */
+
+#ifndef SOURCE_PUGIXML_CPP
+#define SOURCE_PUGIXML_CPP
+
+#include "pugixml.hpp"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <wchar.h>
+
+#ifndef PUGIXML_NO_XPATH
+# include <math.h>
+# include <float.h>
+# ifdef PUGIXML_NO_EXCEPTIONS
+# include <setjmp.h>
+# endif
+#endif
+
+#ifndef PUGIXML_NO_STL
+# include <istream>
+# include <ostream>
+# include <string>
+#endif
+
+// For placement new
+#include <new>
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable: 4127) // conditional expression is constant
+# pragma warning(disable: 4324) // structure was padded due to __declspec(align())
+# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
+# pragma warning(disable: 4702) // unreachable code
+# pragma warning(disable: 4996) // this function or variable may be unsafe
+# pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged
+#endif
+
+#ifdef __INTEL_COMPILER
+# pragma warning(disable: 177) // function was declared but never referenced
+# pragma warning(disable: 279) // controlling expression is constant
+# pragma warning(disable: 1478 1786) // function was declared "deprecated"
+# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type
+#endif
+
+#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
+# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away
+#endif
+
+#ifdef __BORLANDC__
+# pragma option push
+# pragma warn -8008 // condition is always false
+# pragma warn -8066 // unreachable code
+#endif
+
+#ifdef __SNC__
+// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug
+# pragma diag_suppress=178 // function was declared but never referenced
+# pragma diag_suppress=237 // controlling expression is constant
+#endif
+
+// Inlining controls
+#if defined(_MSC_VER) && _MSC_VER >= 1300
+# define PUGI__NO_INLINE __declspec(noinline)
+#elif defined(__GNUC__)
+# define PUGI__NO_INLINE __attribute__((noinline))
+#else
+# define PUGI__NO_INLINE
+#endif
+
+// Simple static assertion
+#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
+
+// Digital Mars C++ bug workaround for passing char loaded from memory via stack
+#ifdef __DMC__
+# define PUGI__DMC_VOLATILE volatile
+#else
+# define PUGI__DMC_VOLATILE
+#endif
+
+// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
+#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
+using std::memcpy;
+using std::memmove;
+#endif
+
+// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
+#if defined(_MSC_VER) && !defined(__S3E__)
+# define PUGI__MSVC_CRT_VERSION _MSC_VER
+#endif
+
+#ifdef PUGIXML_HEADER_ONLY
+# define PUGI__NS_BEGIN namespace pugi { namespace impl {
+# define PUGI__NS_END } }
+# define PUGI__FN inline
+# define PUGI__FN_NO_INLINE inline
+#else
+# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
+# define PUGI__NS_BEGIN namespace pugi { namespace impl {
+# define PUGI__NS_END } }
+# else
+# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
+# define PUGI__NS_END } } }
+# endif
+# define PUGI__FN
+# define PUGI__FN_NO_INLINE PUGI__NO_INLINE
+#endif
+
+// uintptr_t
+#if !defined(_MSC_VER) || _MSC_VER >= 1600
+# include <stdint.h>
+#else
+# ifndef _UINTPTR_T_DEFINED
+// No native uintptr_t in MSVC6 and in some WinCE versions
+typedef size_t uintptr_t;
+#define _UINTPTR_T_DEFINED
+# endif
+PUGI__NS_BEGIN
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+PUGI__NS_END
+#endif
+
+// Memory allocation
+PUGI__NS_BEGIN
+ PUGI__FN void* default_allocate(size_t size)
+ {
+ return malloc(size);
+ }
+
+ PUGI__FN void default_deallocate(void* ptr)
+ {
+ free(ptr);
+ }
+
+ template <typename T>
+ struct xml_memory_management_function_storage
+ {
+ static allocation_function allocate;
+ static deallocation_function deallocate;
+ };
+
+ template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
+ template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
+
+ typedef xml_memory_management_function_storage<int> xml_memory;
+PUGI__NS_END
+
+// String utilities
+PUGI__NS_BEGIN
+ // Get string length
+ PUGI__FN size_t strlength(const char_t* s)
+ {
+ assert(s);
+
+ #ifdef PUGIXML_WCHAR_MODE
+ return wcslen(s);
+ #else
+ return strlen(s);
+ #endif
+ }
+
+ // Compare two strings
+ PUGI__FN bool strequal(const char_t* src, const char_t* dst)
+ {
+ assert(src && dst);
+
+ #ifdef PUGIXML_WCHAR_MODE
+ return wcscmp(src, dst) == 0;
+ #else
+ return strcmp(src, dst) == 0;
+ #endif
+ }
+
+ // Compare lhs with [rhs_begin, rhs_end)
+ PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
+ {
+ for (size_t i = 0; i < count; ++i)
+ if (lhs[i] != rhs[i])
+ return false;
+
+ return lhs[count] == 0;
+ }
+
+#ifdef PUGIXML_WCHAR_MODE
+ // Convert string to wide string, assuming all symbols are ASCII
+ PUGI__FN void widen_ascii(wchar_t* dest, const char* source)
+ {
+ for (const char* i = source; *i; ++i) *dest++ = *i;
+ *dest = 0;
+ }
+#endif
+PUGI__NS_END
+
+#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH)
+// auto_ptr-like buffer holder for exception recovery
+PUGI__NS_BEGIN
+ struct buffer_holder
+ {
+ void* data;
+ void (*deleter)(void*);
+
+ buffer_holder(void* data_, void (*deleter_)(void*)): data(data_), deleter(deleter_)
+ {
+ }
+
+ ~buffer_holder()
+ {
+ if (data) deleter(data);
+ }
+
+ void* release()
+ {
+ void* result = data;
+ data = 0;
+ return result;
+ }
+ };
+PUGI__NS_END
+#endif
+
+PUGI__NS_BEGIN
+ static const size_t xml_memory_page_size =
+ #ifdef PUGIXML_MEMORY_PAGE_SIZE
+ PUGIXML_MEMORY_PAGE_SIZE
+ #else
+ 32768
+ #endif
+ ;
+
+ static const uintptr_t xml_memory_page_alignment = 32;
+ static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1);
+ static const uintptr_t xml_memory_page_name_allocated_mask = 16;
+ static const uintptr_t xml_memory_page_value_allocated_mask = 8;
+ static const uintptr_t xml_memory_page_type_mask = 7;
+
+ struct xml_allocator;
+
+ struct xml_memory_page
+ {
+ static xml_memory_page* construct(void* memory)
+ {
+ if (!memory) return 0; //$ redundant, left for performance
+
+ xml_memory_page* result = static_cast<xml_memory_page*>(memory);
+
+ result->allocator = 0;
+ result->memory = 0;
+ result->prev = 0;
+ result->next = 0;
+ result->busy_size = 0;
+ result->freed_size = 0;
+
+ return result;
+ }
+
+ xml_allocator* allocator;
+
+ void* memory;
+
+ xml_memory_page* prev;
+ xml_memory_page* next;
+
+ size_t busy_size;
+ size_t freed_size;
+
+ char data[1];
+ };
+
+ struct xml_memory_string_header
+ {
+ uint16_t page_offset; // offset from page->data
+ uint16_t full_size; // 0 if string occupies whole page
+ };
+
+ struct xml_allocator
+ {
+ xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
+ {
+ }
+
+ xml_memory_page* allocate_page(size_t data_size)
+ {
+ size_t size = offsetof(xml_memory_page, data) + data_size;
+
+ // allocate block with some alignment, leaving memory for worst-case padding
+ void* memory = xml_memory::allocate(size + xml_memory_page_alignment);
+ if (!memory) return 0;
+
+ // align upwards to page boundary
+ void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1));
+
+ // prepare page structure
+ xml_memory_page* page = xml_memory_page::construct(page_memory);
+
+ page->memory = memory;
+ page->allocator = _root->allocator;
+
+ return page;
+ }
+
+ static void deallocate_page(xml_memory_page* page)
+ {
+ xml_memory::deallocate(page->memory);
+ }
+
+ void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
+
+ void* allocate_memory(size_t size, xml_memory_page*& out_page)
+ {
+ if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page);
+
+ void* buf = _root->data + _busy_size;
+
+ _busy_size += size;
+
+ out_page = _root;
+
+ return buf;
+ }
+
+ void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
+ {
+ if (page == _root) page->busy_size = _busy_size;
+
+ assert(ptr >= page->data && ptr < page->data + page->busy_size);
+ (void)!ptr;
+
+ page->freed_size += size;
+ assert(page->freed_size <= page->busy_size);
+
+ if (page->freed_size == page->busy_size)
+ {
+ if (page->next == 0)
+ {
+ assert(_root == page);
+
+ // top page freed, just reset sizes
+ page->busy_size = page->freed_size = 0;
+ _busy_size = 0;
+ }
+ else
+ {
+ assert(_root != page);
+ assert(page->prev);
+
+ // remove from the list
+ page->prev->next = page->next;
+ page->next->prev = page->prev;
+
+ // deallocate
+ deallocate_page(page);
+ }
+ }
+ }
+
+ char_t* allocate_string(size_t length)
+ {
+ // allocate memory for string and header block
+ size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
+
+ // round size up to pointer alignment boundary
+ size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1);
+
+ xml_memory_page* page;
+ xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));
+
+ if (!header) return 0;
+
+ // setup header
+ ptrdiff_t page_offset = reinterpret_cast<char*>(header) - page->data;
+
+ assert(page_offset >= 0 && page_offset < (1 << 16));
+ header->page_offset = static_cast<uint16_t>(page_offset);
+
+ // full_size == 0 for large strings that occupy the whole page
+ assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0));
+ header->full_size = static_cast<uint16_t>(full_size < (1 << 16) ? full_size : 0);
+
+ // round-trip through void* to avoid 'cast increases required alignment of target type' warning
+ // header is guaranteed a pointer-sized alignment, which should be enough for char_t
+ return static_cast<char_t*>(static_cast<void*>(header + 1));
+ }
+
+ void deallocate_string(char_t* string)
+ {
+ // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
+ // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string
+
+ // get header
+ xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
+
+ // deallocate
+ size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset;
+ xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
+
+ // if full_size == 0 then this string occupies the whole page
+ size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size;
+
+ deallocate_memory(header, full_size, page);
+ }
+
+ xml_memory_page* _root;
+ size_t _busy_size;
+ };
+
+ PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
+ {
+ const size_t large_allocation_threshold = xml_memory_page_size / 4;
+
+ xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);
+ out_page = page;
+
+ if (!page) return 0;
+
+ if (size <= large_allocation_threshold)
+ {
+ _root->busy_size = _busy_size;
+
+ // insert page at the end of linked list
+ page->prev = _root;
+ _root->next = page;
+ _root = page;
+
+ _busy_size = size;
+ }
+ else
+ {
+ // insert page before the end of linked list, so that it is deleted as soon as possible
+ // the last page is not deleted even if it's empty (see deallocate_memory)
+ assert(_root->prev);
+
+ page->prev = _root->prev;
+ page->next = _root;
+
+ _root->prev->next = page;
+ _root->prev = page;
+ }
+
+ // allocate inside page
+ page->busy_size = size;
+
+ return page->data;
+ }
+PUGI__NS_END
+
+namespace pugi
+{
+ /// A 'name=value' XML attribute structure.
+ struct xml_attribute_struct
+ {
+ /// Default ctor
+ xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast<uintptr_t>(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0)
+ {
+ }
+
+ uintptr_t header;
+
+ char_t* name; ///< Pointer to attribute name.
+ char_t* value; ///< Pointer to attribute value.
+
+ xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list)
+ xml_attribute_struct* next_attribute; ///< Next attribute
+ };
+
+ /// An XML document tree node.
+ struct xml_node_struct
+ {
+ /// Default ctor
+ /// \param type - node type
+ xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast<uintptr_t>(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
+ {
+ }
+
+ uintptr_t header;
+
+ xml_node_struct* parent; ///< Pointer to parent
+
+ char_t* name; ///< Pointer to element name.
+ char_t* value; ///< Pointer to any associated string data.
+
+ xml_node_struct* first_child; ///< First child
+
+ xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list)
+ xml_node_struct* next_sibling; ///< Right brother
+
+ xml_attribute_struct* first_attribute; ///< First attribute
+ };
+}
+
+PUGI__NS_BEGIN
+ struct xml_document_struct: public xml_node_struct, public xml_allocator
+ {
+ xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0)
+ {
+ }
+
+ const char_t* buffer;
+ };
+
+ inline xml_allocator& get_allocator(const xml_node_struct* node)
+ {
+ assert(node);
+
+ return *reinterpret_cast<xml_memory_page*>(node->header & xml_memory_page_pointer_mask)->allocator;
+ }
+PUGI__NS_END
+
+// Low-level DOM operations
+PUGI__NS_BEGIN
+ inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)
+ {
+ xml_memory_page* page;
+ void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page);
+
+ return new (memory) xml_attribute_struct(page);
+ }
+
+ inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)
+ {
+ xml_memory_page* page;
+ void* memory = alloc.allocate_memory(sizeof(xml_node_struct), page);
+
+ return new (memory) xml_node_struct(page, type);
+ }
+
+ inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
+ {
+ uintptr_t header = a->header;
+
+ if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name);
+ if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value);
+
+ alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
+ }
+
+ inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
+ {
+ uintptr_t header = n->header;
+
+ if (header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name);
+ if (header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value);
+
+ for (xml_attribute_struct* attr = n->first_attribute; attr; )
+ {
+ xml_attribute_struct* next = attr->next_attribute;
+
+ destroy_attribute(attr, alloc);
+
+ attr = next;
+ }
+
+ for (xml_node_struct* child = n->first_child; child; )
+ {
+ xml_node_struct* next = child->next_sibling;
+
+ destroy_node(child, alloc);
+
+ child = next;
+ }
+
+ alloc.deallocate_memory(n, sizeof(xml_node_struct), reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask));
+ }
+
+ PUGI__FN_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
+ {
+ xml_node_struct* child = allocate_node(alloc, type);
+ if (!child) return 0;
+
+ child->parent = node;
+
+ xml_node_struct* first_child = node->first_child;
+
+ if (first_child)
+ {
+ xml_node_struct* last_child = first_child->prev_sibling_c;
+
+ last_child->next_sibling = child;
+ child->prev_sibling_c = last_child;
+ first_child->prev_sibling_c = child;
+ }
+ else
+ {
+ node->first_child = child;
+ child->prev_sibling_c = child;
+ }
+
+ return child;
+ }
+
+ PUGI__FN_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc)
+ {
+ xml_attribute_struct* a = allocate_attribute(alloc);
+ if (!a) return 0;
+
+ xml_attribute_struct* first_attribute = node->first_attribute;
+
+ if (first_attribute)
+ {
+ xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c;
+
+ last_attribute->next_attribute = a;
+ a->prev_attribute_c = last_attribute;
+ first_attribute->prev_attribute_c = a;
+ }
+ else
+ {
+ node->first_attribute = a;
+ a->prev_attribute_c = a;
+ }
+
+ return a;
+ }
+PUGI__NS_END
+
+// Helper classes for code generation
+PUGI__NS_BEGIN
+ struct opt_false
+ {
+ enum { value = 0 };
+ };
+
+ struct opt_true
+ {
+ enum { value = 1 };
+ };
+PUGI__NS_END
+
+// Unicode utilities
+PUGI__NS_BEGIN
+ inline uint16_t endian_swap(uint16_t value)
+ {
+ return static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8));
+ }
+
+ inline uint32_t endian_swap(uint32_t value)
+ {
+ return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24);
+ }
+
+ struct utf8_counter
+ {
+ typedef size_t value_type;
+
+ static value_type low(value_type result, uint32_t ch)
+ {
+ // U+0000..U+007F
+ if (ch < 0x80) return result + 1;
+ // U+0080..U+07FF
+ else if (ch < 0x800) return result + 2;
+ // U+0800..U+FFFF
+ else return result + 3;
+ }
+
+ static value_type high(value_type result, uint32_t)
+ {
+ // U+10000..U+10FFFF
+ return result + 4;
+ }
+ };
+
+ struct utf8_writer
+ {
+ typedef uint8_t* value_type;
+
+ static value_type low(value_type result, uint32_t ch)
+ {
+ // U+0000..U+007F
+ if (ch < 0x80)
+ {
+ *result = static_cast<uint8_t>(ch);
+ return result + 1;
+ }
+ // U+0080..U+07FF
+ else if (ch < 0x800)
+ {
+ result[0] = static_cast<uint8_t>(0xC0 | (ch >> 6));
+ result[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
+ return result + 2;
+ }
+ // U+0800..U+FFFF
+ else
+ {
+ result[0] = static_cast<uint8_t>(0xE0 | (ch >> 12));
+ result[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
+ result[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
+ return result + 3;
+ }
+ }
+
+ static value_type high(value_type result, uint32_t ch)
+ {
+ // U+10000..U+10FFFF
+ result[0] = static_cast<uint8_t>(0xF0 | (ch >> 18));
+ result[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F));
+ result[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
+ result[3] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
+ return result + 4;
+ }
+
+ static value_type any(value_type result, uint32_t ch)
+ {
+ return (ch < 0x10000) ? low(result, ch) : high(result, ch);
+ }
+ };
+
+ struct utf16_counter
+ {
+ typedef size_t value_type;
+
+ static value_type low(value_type result, uint32_t)
+ {
+ return result + 1;
+ }
+
+ static value_type high(value_type result, uint32_t)
+ {
+ return result + 2;
+ }
+ };
+
+ struct utf16_writer
+ {
+ typedef uint16_t* value_type;
+
+ static value_type low(value_type result, uint32_t ch)
+ {
+ *result = static_cast<uint16_t>(ch);
+
+ return result + 1;
+ }
+
+ static value_type high(value_type result, uint32_t ch)
+ {
+ uint32_t msh = static_cast<uint32_t>(ch - 0x10000) >> 10;
+ uint32_t lsh = static_cast<uint32_t>(ch - 0x10000) & 0x3ff;
+
+ result[0] = static_cast<uint16_t>(0xD800 + msh);
+ result[1] = static_cast<uint16_t>(0xDC00 + lsh);
+
+ return result + 2;
+ }
+
+ static value_type any(value_type result, uint32_t ch)
+ {
+ return (ch < 0x10000) ? low(result, ch) : high(result, ch);
+ }
+ };
+
+ struct utf32_counter
+ {
+ typedef size_t value_type;
+
+ static value_type low(value_type result, uint32_t)
+ {
+ return result + 1;
+ }
+
+ static value_type high(value_type result, uint32_t)
+ {
+ return result + 1;
+ }
+ };
+
+ struct utf32_writer
+ {
+ typedef uint32_t* value_type;
+
+ static value_type low(value_type result, uint32_t ch)
+ {
+ *result = ch;
+
+ return result + 1;
+ }
+
+ static value_type high(value_type result, uint32_t ch)
+ {
+ *result = ch;
+
+ return result + 1;
+ }
+
+ static value_type any(value_type result, uint32_t ch)
+ {
+ *result = ch;
+
+ return result + 1;
+ }
+ };
+
+ struct latin1_writer
+ {
+ typedef uint8_t* value_type;
+
+ static value_type low(value_type result, uint32_t ch)
+ {
+ *result = static_cast<uint8_t>(ch > 255 ? '?' : ch);
+
+ return result + 1;
+ }
+
+ static value_type high(value_type result, uint32_t ch)
+ {
+ (void)ch;
+
+ *result = '?';
+
+ return result + 1;
+ }
+ };
+
+ template <size_t size> struct wchar_selector;
+
+ template <> struct wchar_selector<2>
+ {
+ typedef uint16_t type;
+ typedef utf16_counter counter;
+ typedef utf16_writer writer;
+ };
+
+ template <> struct wchar_selector<4>
+ {
+ typedef uint32_t type;
+ typedef utf32_counter counter;
+ typedef utf32_writer writer;
+ };
+
+ typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
+ typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
+
+ template <typename Traits, typename opt_swap = opt_false> struct utf_decoder
+ {
+ static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result)
+ {
+ const uint8_t utf8_byte_mask = 0x3f;
+
+ while (size)
+ {
+ uint8_t lead = *data;
+
+ // 0xxxxxxx -> U+0000..U+007F
+ if (lead < 0x80)
+ {
+ result = Traits::low(result, lead);
+ data += 1;
+ size -= 1;
+
+ // process aligned single-byte (ascii) blocks
+ if ((reinterpret_cast<uintptr_t>(data) & 3) == 0)
+ {
+ // round-trip through void* to silence 'cast increases required alignment of target type' warnings
+ while (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(data)) & 0x80808080) == 0)
+ {
+ result = Traits::low(result, data[0]);
+ result = Traits::low(result, data[1]);
+ result = Traits::low(result, data[2]);
+ result = Traits::low(result, data[3]);
+ data += 4;
+ size -= 4;
+ }
+ }
+ }
+ // 110xxxxx -> U+0080..U+07FF
+ else if (static_cast<unsigned int>(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80)
+ {
+ result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask));
+ data += 2;
+ size -= 2;
+ }
+ // 1110xxxx -> U+0800-U+FFFF
+ else if (static_cast<unsigned int>(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80)
+ {
+ result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask));
+ data += 3;
+ size -= 3;
+ }
+ // 11110xxx -> U+10000..U+10FFFF
+ else if (static_cast<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)
+ {
+ result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));
+ data += 4;
+ size -= 4;
+ }
+ // 10xxxxxx or 11111xxx -> invalid
+ else
+ {
+ data += 1;
+ size -= 1;
+ }
+ }
+
+ return result;
+ }
+
+ static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result)
+ {
+ const uint16_t* end = data + size;
+
+ while (data < end)
+ {
+ uint16_t lead = opt_swap::value ? endian_swap(*data) : *data;
+
+ // U+0000..U+D7FF
+ if (lead < 0xD800)
+ {
+ result = Traits::low(result, lead);
+ data += 1;
+ }
+ // U+E000..U+FFFF
+ else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000)
+ {
+ result = Traits::low(result, lead);
+ data += 1;
+ }
+ // surrogate pair lead
+ else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && data + 1 < end)
+ {
+ uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];
+
+ if (static_cast<unsigned int>(next - 0xDC00) < 0x400)
+ {
+ result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
+ data += 2;
+ }
+ else
+ {
+ data += 1;
+ }
+ }
+ else
+ {
+ data += 1;
+ }
+ }
+
+ return result;
+ }
+
+ static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result)
+ {
+ const uint32_t* end = data + size;
+
+ while (data < end)
+ {
+ uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;
+
+ // U+0000..U+FFFF
+ if (lead < 0x10000)
+ {
+ result = Traits::low(result, lead);
+ data += 1;
+ }
+ // U+10000..U+10FFFF
+ else
+ {
+ result = Traits::high(result, lead);
+ data += 1;
+ }
+ }
+
+ return result;
+ }
+
+ static inline typename Traits::value_type decode_latin1_block(const uint8_t* data, size_t size, typename Traits::value_type result)
+ {
+ for (size_t i = 0; i < size; ++i)
+ {
+ result = Traits::low(result, data[i]);
+ }
+
+ return result;
+ }
+
+ static inline typename Traits::value_type decode_wchar_block_impl(const uint16_t* data, size_t size, typename Traits::value_type result)
+ {
+ return decode_utf16_block(data, size, result);
+ }
+
+ static inline typename Traits::value_type decode_wchar_block_impl(const uint32_t* data, size_t size, typename Traits::value_type result)
+ {
+ return decode_utf32_block(data, size, result);
+ }
+
+ static inline typename Traits::value_type decode_wchar_block(const wchar_t* data, size_t size, typename Traits::value_type result)
+ {
+ return decode_wchar_block_impl(reinterpret_cast<const wchar_selector<sizeof(wchar_t)>::type*>(data), size, result);
+ }
+ };
+
+ template <typename T> PUGI__FN void convert_utf_endian_swap(T* result, const T* data, size_t length)
+ {
+ for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]);
+ }
+
+#ifdef PUGIXML_WCHAR_MODE
+ PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
+ {
+ for (size_t i = 0; i < length; ++i) result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));
+ }
+#endif
+PUGI__NS_END
+
+PUGI__NS_BEGIN
+ enum chartype_t
+ {
+ ct_parse_pcdata = 1, // \0, &, \r, <
+ ct_parse_attr = 2, // \0, &, \r, ', "
+ ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab
+ ct_space = 8, // \r, \n, space, tab
+ ct_parse_cdata = 16, // \0, ], >, \r
+ ct_parse_comment = 32, // \0, -, >, \r
+ ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .
+ ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, :
+ };
+
+ static const unsigned char chartype_table[256] =
+ {
+ 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
+ 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63
+ 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95
+ 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127
+
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192
+ };
+
+ enum chartypex_t
+ {
+ ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, >
+ ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, "
+ ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _
+ ctx_digit = 8, // 0-9
+ ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, .
+ };
+
+ static const unsigned char chartypex_table[256] =
+ {
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31
+ 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63
+
+ 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95
+ 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127
+
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20
+ };
+
+#ifdef PUGIXML_WCHAR_MODE
+ #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))
+#else
+ #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))
+#endif
+
+ #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table)
+ #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table)
+
+ PUGI__FN bool is_little_endian()
+ {
+ unsigned int ui = 1;
+
+ return *reinterpret_cast<unsigned char*>(&ui) == 1;
+ }
+
+ PUGI__FN xml_encoding get_wchar_encoding()
+ {
+ PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
+
+ if (sizeof(wchar_t) == 2)
+ return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+ else
+ return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+ }
+
+ PUGI__FN xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
+ {
+ // look for BOM in first few bytes
+ if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;
+ if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;
+ if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;
+ if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;
+ if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;
+
+ // look for <, <? or <?xm in various encodings
+ if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be;
+ if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;
+ if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be;
+ if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;
+ if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d) return encoding_utf8;
+
+ // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)
+ if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;
+ if (d0 == 0x3c && d1 == 0) return encoding_utf16_le;
+
+ // no known BOM detected, assume utf8
+ return encoding_utf8;
+ }
+
+ PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)
+ {
+ // replace wchar encoding with utf implementation
+ if (encoding == encoding_wchar) return get_wchar_encoding();
+
+ // replace utf16 encoding with utf16 with specific endianness
+ if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+ // replace utf32 encoding with utf32 with specific endianness
+ if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+ // only do autodetection if no explicit encoding is requested
+ if (encoding != encoding_auto) return encoding;
+
+ // skip encoding autodetection if input buffer is too small
+ if (size < 4) return encoding_utf8;
+
+ // try to guess encoding (based on XML specification, Appendix F.1)
+ const uint8_t* data = static_cast<const uint8_t*>(contents);
+
+ PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];
+
+ return guess_buffer_encoding(d0, d1, d2, d3);
+ }
+
+ PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+ {
+ if (is_mutable)
+ {
+ out_buffer = static_cast<char_t*>(const_cast<void*>(contents));
+ }
+ else
+ {
+ void* buffer = xml_memory::allocate(size > 0 ? size : 1);
+ if (!buffer) return false;
+
+ memcpy(buffer, contents, size);
+
+ out_buffer = static_cast<char_t*>(buffer);
+ }
+
+ out_length = size / sizeof(char_t);
+
+ return true;
+ }
+
+#ifdef PUGIXML_WCHAR_MODE
+ PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)
+ {
+ return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||
+ (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);
+ }
+
+ PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+ {
+ const char_t* data = static_cast<const char_t*>(contents);
+
+ if (is_mutable)
+ {
+ out_buffer = const_cast<char_t*>(data);
+ }
+ else
+ {
+ out_buffer = static_cast<char_t*>(xml_memory::allocate(size > 0 ? size : 1));
+ if (!out_buffer) return false;
+ }
+
+ out_length = size / sizeof(char_t);
+
+ convert_wchar_endian_swap(out_buffer, data, out_length);
+
+ return true;
+ }
+
+ PUGI__FN bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
+ {
+ const uint8_t* data = static_cast<const uint8_t*>(contents);
+
+ // first pass: get length in wchar_t units
+ out_length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // second pass: convert utf8 input to wchar_t
+ wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
+ wchar_writer::value_type out_end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, out_begin);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
+ {
+ const uint16_t* data = static_cast<const uint16_t*>(contents);
+ size_t length = size / sizeof(uint16_t);
+
+ // first pass: get length in wchar_t units
+ out_length = utf_decoder<wchar_counter, opt_swap>::decode_utf16_block(data, length, 0);
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // second pass: convert utf16 input to wchar_t
+ wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
+ wchar_writer::value_type out_end = utf_decoder<wchar_writer, opt_swap>::decode_utf16_block(data, length, out_begin);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
+ {
+ const uint32_t* data = static_cast<const uint32_t*>(contents);
+ size_t length = size / sizeof(uint32_t);
+
+ // first pass: get length in wchar_t units
+ out_length = utf_decoder<wchar_counter, opt_swap>::decode_utf32_block(data, length, 0);
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // second pass: convert utf32 input to wchar_t
+ wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
+ wchar_writer::value_type out_end = utf_decoder<wchar_writer, opt_swap>::decode_utf32_block(data, length, out_begin);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size)
+ {
+ const uint8_t* data = static_cast<const uint8_t*>(contents);
+
+ // get length in wchar_t units
+ out_length = size;
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // convert latin1 input to wchar_t
+ wchar_writer::value_type out_begin = reinterpret_cast<wchar_writer::value_type>(out_buffer);
+ wchar_writer::value_type out_end = utf_decoder<wchar_writer>::decode_latin1_block(data, size, out_begin);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
+ {
+ // get native encoding
+ xml_encoding wchar_encoding = get_wchar_encoding();
+
+ // fast path: no conversion required
+ if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+
+ // only endian-swapping is required
+ if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
+
+ // source encoding is utf8
+ if (encoding == encoding_utf8) return convert_buffer_utf8(out_buffer, out_length, contents, size);
+
+ // source encoding is utf16
+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+ {
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+ return (native_encoding == encoding) ?
+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
+ }
+
+ // source encoding is utf32
+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+ {
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+ return (native_encoding == encoding) ?
+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
+ }
+
+ // source encoding is latin1
+ if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size);
+
+ assert(!"Invalid encoding");
+ return false;
+ }
+#else
+ template <typename opt_swap> PUGI__FN bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
+ {
+ const uint16_t* data = static_cast<const uint16_t*>(contents);
+ size_t length = size / sizeof(uint16_t);
+
+ // first pass: get length in utf8 units
+ out_length = utf_decoder<utf8_counter, opt_swap>::decode_utf16_block(data, length, 0);
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // second pass: convert utf16 input to utf8
+ uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
+ uint8_t* out_end = utf_decoder<utf8_writer, opt_swap>::decode_utf16_block(data, length, out_begin);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ template <typename opt_swap> PUGI__FN bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap)
+ {
+ const uint32_t* data = static_cast<const uint32_t*>(contents);
+ size_t length = size / sizeof(uint32_t);
+
+ // first pass: get length in utf8 units
+ out_length = utf_decoder<utf8_counter, opt_swap>::decode_utf32_block(data, length, 0);
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // second pass: convert utf32 input to utf8
+ uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
+ uint8_t* out_end = utf_decoder<utf8_writer, opt_swap>::decode_utf32_block(data, length, out_begin);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)
+ {
+ for (size_t i = 0; i < size; ++i)
+ if (data[i] > 127)
+ return i;
+
+ return size;
+ }
+
+ PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+ {
+ const uint8_t* data = static_cast<const uint8_t*>(contents);
+
+ // get size of prefix that does not need utf8 conversion
+ size_t prefix_length = get_latin1_7bit_prefix_length(data, size);
+ assert(prefix_length <= size);
+
+ const uint8_t* postfix = data + prefix_length;
+ size_t postfix_length = size - prefix_length;
+
+ // if no conversion is needed, just return the original buffer
+ if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+
+ // first pass: get length in utf8 units
+ out_length = prefix_length + utf_decoder<utf8_counter>::decode_latin1_block(postfix, postfix_length, 0);
+
+ // allocate buffer of suitable length
+ out_buffer = static_cast<char_t*>(xml_memory::allocate((out_length > 0 ? out_length : 1) * sizeof(char_t)));
+ if (!out_buffer) return false;
+
+ // second pass: convert latin1 input to utf8
+ memcpy(out_buffer, data, prefix_length);
+
+ uint8_t* out_begin = reinterpret_cast<uint8_t*>(out_buffer);
+ uint8_t* out_end = utf_decoder<utf8_writer>::decode_latin1_block(postfix, postfix_length, out_begin + prefix_length);
+
+ assert(out_end == out_begin + out_length);
+ (void)!out_end;
+
+ return true;
+ }
+
+ PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
+ {
+ // fast path: no conversion required
+ if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+
+ // source encoding is utf16
+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+ {
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+ return (native_encoding == encoding) ?
+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_false()) :
+ convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true());
+ }
+
+ // source encoding is utf32
+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+ {
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+ return (native_encoding == encoding) ?
+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_false()) :
+ convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true());
+ }
+
+ // source encoding is latin1
+ if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
+
+ assert(!"Invalid encoding");
+ return false;
+ }
+#endif
+
+ PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)
+ {
+ // get length in utf8 characters
+ return utf_decoder<utf8_counter>::decode_wchar_block(str, length, 0);
+ }
+
+ PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
+ {
+ // convert to utf8
+ uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
+ uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(str, length, begin);
+
+ assert(begin + size == end);
+ (void)!end;
+
+ // zero-terminate
+ buffer[size] = 0;
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)
+ {
+ // first pass: get length in utf8 characters
+ size_t size = as_utf8_begin(str, length);
+
+ // allocate resulting string
+ std::string result;
+ result.resize(size);
+
+ // second pass: convert to utf8
+ if (size > 0) as_utf8_end(&result[0], size, str, length);
+
+ return result;
+ }
+
+ PUGI__FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)
+ {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(str);
+
+ // first pass: get length in wchar_t units
+ size_t length = utf_decoder<wchar_counter>::decode_utf8_block(data, size, 0);
+
+ // allocate resulting string
+ std::basic_string<wchar_t> result;
+ result.resize(length);
+
+ // second pass: convert to wchar_t
+ if (length > 0)
+ {
+ wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);
+ wchar_writer::value_type end = utf_decoder<wchar_writer>::decode_utf8_block(data, size, begin);
+
+ assert(begin + length == end);
+ (void)!end;
+ }
+
+ return result;
+ }
+#endif
+
+ inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target)
+ {
+ assert(target);
+ size_t target_length = strlength(target);
+
+ // always reuse document buffer memory if possible
+ if (!allocated) return target_length >= length;
+
+ // reuse heap memory if waste is not too great
+ const size_t reuse_threshold = 32;
+
+ return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);
+ }
+
+ PUGI__FN bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source)
+ {
+ size_t source_length = strlength(source);
+
+ if (source_length == 0)
+ {
+ // empty string and null pointer are equivalent, so just deallocate old memory
+ xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
+
+ if (header & header_mask) alloc->deallocate_string(dest);
+
+ // mark the string as not allocated
+ dest = 0;
+ header &= ~header_mask;
+
+ return true;
+ }
+ else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest))
+ {
+ // we can reuse old buffer, so just copy the new data (including zero terminator)
+ memcpy(dest, source, (source_length + 1) * sizeof(char_t));
+
+ return true;
+ }
+ else
+ {
+ xml_allocator* alloc = reinterpret_cast<xml_memory_page*>(header & xml_memory_page_pointer_mask)->allocator;
+
+ // allocate new buffer
+ char_t* buf = alloc->allocate_string(source_length + 1);
+ if (!buf) return false;
+
+ // copy the string (including zero terminator)
+ memcpy(buf, source, (source_length + 1) * sizeof(char_t));
+
+ // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)
+ if (header & header_mask) alloc->deallocate_string(dest);
+
+ // the string is now allocated, so set the flag
+ dest = buf;
+ header |= header_mask;
+
+ return true;
+ }
+ }
+
+ struct gap
+ {
+ char_t* end;
+ size_t size;
+
+ gap(): end(0), size(0)
+ {
+ }
+
+ // Push new gap, move s count bytes further (skipping the gap).
+ // Collapse previous gap.
+ void push(char_t*& s, size_t count)
+ {
+ if (end) // there was a gap already; collapse it
+ {
+ // Move [old_gap_end, new_gap_start) to [old_gap_start, ...)
+ assert(s >= end);
+ memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));
+ }
+
+ s += count; // end of current gap
+
+ // "merge" two gaps
+ end = s;
+ size += count;
+ }
+
+ // Collapse all gaps, return past-the-end pointer
+ char_t* flush(char_t* s)
+ {
+ if (end)
+ {
+ // Move [old_gap_end, current_pos) to [old_gap_start, ...)
+ assert(s >= end);
+ memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));
+
+ return s - size;
+ }
+ else return s;
+ }
+ };
+
+ PUGI__FN char_t* strconv_escape(char_t* s, gap& g)
+ {
+ char_t* stre = s + 1;
+
+ switch (*stre)
+ {
+ case '#': // &#...
+ {
+ unsigned int ucsc = 0;
+
+ if (stre[1] == 'x') // &#x... (hex code)
+ {
+ stre += 2;
+
+ char_t ch = *stre;
+
+ if (ch == ';') return stre;
+
+ for (;;)
+ {
+ if (static_cast<unsigned int>(ch - '0') <= 9)
+ ucsc = 16 * ucsc + (ch - '0');
+ else if (static_cast<unsigned int>((ch | ' ') - 'a') <= 5)
+ ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10);
+ else if (ch == ';')
+ break;
+ else // cancel
+ return stre;
+
+ ch = *++stre;
+ }
+
+ ++stre;
+ }
+ else // &#... (dec code)
+ {
+ char_t ch = *++stre;
+
+ if (ch == ';') return stre;
+
+ for (;;)
+ {
+ if (static_cast<unsigned int>(ch - '0') <= 9)
+ ucsc = 10 * ucsc + (ch - '0');
+ else if (ch == ';')
+ break;
+ else // cancel
+ return stre;
+
+ ch = *++stre;
+ }
+
+ ++stre;
+ }
+
+ #ifdef PUGIXML_WCHAR_MODE
+ s = reinterpret_cast<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc));
+ #else
+ s = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc));
+ #endif
+
+ g.push(s, stre - s);
+ return stre;
+ }
+
+ case 'a': // &a
+ {
+ ++stre;
+
+ if (*stre == 'm') // &am
+ {
+ if (*++stre == 'p' && *++stre == ';') // &amp;
+ {
+ *s++ = '&';
+ ++stre;
+
+ g.push(s, stre - s);
+ return stre;
+ }
+ }
+ else if (*stre == 'p') // &ap
+ {
+ if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // &apos;
+ {
+ *s++ = '\'';
+ ++stre;
+
+ g.push(s, stre - s);
+ return stre;
+ }
+ }
+ break;
+ }
+
+ case 'g': // &g
+ {
+ if (*++stre == 't' && *++stre == ';') // &gt;
+ {
+ *s++ = '>';
+ ++stre;
+
+ g.push(s, stre - s);
+ return stre;
+ }
+ break;
+ }
+
+ case 'l': // &l
+ {
+ if (*++stre == 't' && *++stre == ';') // &lt;
+ {
+ *s++ = '<';
+ ++stre;
+
+ g.push(s, stre - s);
+ return stre;
+ }
+ break;
+ }
+
+ case 'q': // &q
+ {
+ if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // &quot;
+ {
+ *s++ = '"';
+ ++stre;
+
+ g.push(s, stre - s);
+ return stre;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return stre;
+ }
+
+ // Utility macro for last character handling
+ #define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
+
+ PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
+ {
+ gap g;
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_comment)) ++s;
+
+ if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
+ {
+ *s++ = '\n'; // replace first one with 0x0a
+
+ if (*s == '\n') g.push(s, 1);
+ }
+ else if (s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')) // comment ends here
+ {
+ *g.flush(s) = 0;
+
+ return s + (s[2] == '>' ? 3 : 2);
+ }
+ else if (*s == 0)
+ {
+ return 0;
+ }
+ else ++s;
+ }
+ }
+
+ PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
+ {
+ gap g;
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_cdata)) ++s;
+
+ if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
+ {
+ *s++ = '\n'; // replace first one with 0x0a
+
+ if (*s == '\n') g.push(s, 1);
+ }
+ else if (s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')) // CDATA ends here
+ {
+ *g.flush(s) = 0;
+
+ return s + 1;
+ }
+ else if (*s == 0)
+ {
+ return 0;
+ }
+ else ++s;
+ }
+ }
+
+ typedef char_t* (*strconv_pcdata_t)(char_t*);
+
+ template <typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
+ {
+ static char_t* parse(char_t* s)
+ {
+ gap g;
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;
+
+ if (*s == '<') // PCDATA ends here
+ {
+ *g.flush(s) = 0;
+
+ return s + 1;
+ }
+ else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
+ {
+ *s++ = '\n'; // replace first one with 0x0a
+
+ if (*s == '\n') g.push(s, 1);
+ }
+ else if (opt_escape::value && *s == '&')
+ {
+ s = strconv_escape(s, g);
+ }
+ else if (*s == 0)
+ {
+ return s;
+ }
+ else ++s;
+ }
+ }
+ };
+
+ PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
+ {
+ PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20);
+
+ switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes)
+ {
+ case 0: return strconv_pcdata_impl<opt_false, opt_false>::parse;
+ case 1: return strconv_pcdata_impl<opt_false, opt_true>::parse;
+ case 2: return strconv_pcdata_impl<opt_true, opt_false>::parse;
+ case 3: return strconv_pcdata_impl<opt_true, opt_true>::parse;
+ default: return 0; // should not get here
+ }
+ }
+
+ typedef char_t* (*strconv_attribute_t)(char_t*, char_t);
+
+ template <typename opt_escape> struct strconv_attribute_impl
+ {
+ static char_t* parse_wnorm(char_t* s, char_t end_quote)
+ {
+ gap g;
+
+ // trim leading whitespaces
+ if (PUGI__IS_CHARTYPE(*s, ct_space))
+ {
+ char_t* str = s;
+
+ do ++str;
+ while (PUGI__IS_CHARTYPE(*str, ct_space));
+
+ g.push(s, str - s);
+ }
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s;
+
+ if (*s == end_quote)
+ {
+ char_t* str = g.flush(s);
+
+ do *str-- = 0;
+ while (PUGI__IS_CHARTYPE(*str, ct_space));
+
+ return s + 1;
+ }
+ else if (PUGI__IS_CHARTYPE(*s, ct_space))
+ {
+ *s++ = ' ';
+
+ if (PUGI__IS_CHARTYPE(*s, ct_space))
+ {
+ char_t* str = s + 1;
+ while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;
+
+ g.push(s, str - s);
+ }
+ }
+ else if (opt_escape::value && *s == '&')
+ {
+ s = strconv_escape(s, g);
+ }
+ else if (!*s)
+ {
+ return 0;
+ }
+ else ++s;
+ }
+ }
+
+ static char_t* parse_wconv(char_t* s, char_t end_quote)
+ {
+ gap g;
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s;
+
+ if (*s == end_quote)
+ {
+ *g.flush(s) = 0;
+
+ return s + 1;
+ }
+ else if (PUGI__IS_CHARTYPE(*s, ct_space))
+ {
+ if (*s == '\r')
+ {
+ *s++ = ' ';
+
+ if (*s == '\n') g.push(s, 1);
+ }
+ else *s++ = ' ';
+ }
+ else if (opt_escape::value && *s == '&')
+ {
+ s = strconv_escape(s, g);
+ }
+ else if (!*s)
+ {
+ return 0;
+ }
+ else ++s;
+ }
+ }
+
+ static char_t* parse_eol(char_t* s, char_t end_quote)
+ {
+ gap g;
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
+
+ if (*s == end_quote)
+ {
+ *g.flush(s) = 0;
+
+ return s + 1;
+ }
+ else if (*s == '\r')
+ {
+ *s++ = '\n';
+
+ if (*s == '\n') g.push(s, 1);
+ }
+ else if (opt_escape::value && *s == '&')
+ {
+ s = strconv_escape(s, g);
+ }
+ else if (!*s)
+ {
+ return 0;
+ }
+ else ++s;
+ }
+ }
+
+ static char_t* parse_simple(char_t* s, char_t end_quote)
+ {
+ gap g;
+
+ while (true)
+ {
+ while (!PUGI__IS_CHARTYPE(*s, ct_parse_attr)) ++s;
+
+ if (*s == end_quote)
+ {
+ *g.flush(s) = 0;
+
+ return s + 1;
+ }
+ else if (opt_escape::value && *s == '&')
+ {
+ s = strconv_escape(s, g);
+ }
+ else if (!*s)
+ {
+ return 0;
+ }
+ else ++s;
+ }
+ }
+ };
+
+ PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
+ {
+ PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
+
+ switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)
+ {
+ case 0: return strconv_attribute_impl<opt_false>::parse_simple;
+ case 1: return strconv_attribute_impl<opt_true>::parse_simple;
+ case 2: return strconv_attribute_impl<opt_false>::parse_eol;
+ case 3: return strconv_attribute_impl<opt_true>::parse_eol;
+ case 4: return strconv_attribute_impl<opt_false>::parse_wconv;
+ case 5: return strconv_attribute_impl<opt_true>::parse_wconv;
+ case 6: return strconv_attribute_impl<opt_false>::parse_wconv;
+ case 7: return strconv_attribute_impl<opt_true>::parse_wconv;
+ case 8: return strconv_attribute_impl<opt_false>::parse_wnorm;
+ case 9: return strconv_attribute_impl<opt_true>::parse_wnorm;
+ case 10: return strconv_attribute_impl<opt_false>::parse_wnorm;
+ case 11: return strconv_attribute_impl<opt_true>::parse_wnorm;
+ case 12: return strconv_attribute_impl<opt_false>::parse_wnorm;
+ case 13: return strconv_attribute_impl<opt_true>::parse_wnorm;
+ case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
+ case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;
+ default: return 0; // should not get here
+ }
+ }
+
+ inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0)
+ {
+ xml_parse_result result;
+ result.status = status;
+ result.offset = offset;
+
+ return result;
+ }
+
+ struct xml_parser
+ {
+ xml_allocator alloc;
+ char_t* error_offset;
+ xml_parse_status error_status;
+
+ // Parser utilities.
+ #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
+ #define PUGI__OPTSET(OPT) ( optmsk & (OPT) )
+ #define PUGI__PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
+ #define PUGI__POPNODE() { cursor = cursor->parent; }
+ #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; }
+ #define PUGI__SCANWHILE(X) { while ((X)) ++s; }
+ #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; }
+ #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast<char_t*>(0)
+ #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); }
+
+ xml_parser(const xml_allocator& alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
+ {
+ }
+
+ // DOCTYPE consists of nested sections of the following possible types:
+ // <!-- ... -->, <? ... ?>, "...", '...'
+ // <![...]]>
+ // <!...>
+ // First group can not contain nested groups
+ // Second group can contain nested groups of the same type
+ // Third group can contain all other groups
+ char_t* parse_doctype_primitive(char_t* s)
+ {
+ if (*s == '"' || *s == '\'')
+ {
+ // quoted string
+ char_t ch = *s++;
+ PUGI__SCANFOR(*s == ch);
+ if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+ s++;
+ }
+ else if (s[0] == '<' && s[1] == '?')
+ {
+ // <? ... ?>
+ s += 2;
+ PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
+ if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+ s += 2;
+ }
+ else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')
+ {
+ s += 4;
+ PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
+ if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+ s += 4;
+ }
+ else PUGI__THROW_ERROR(status_bad_doctype, s);
+
+ return s;
+ }
+
+ char_t* parse_doctype_ignore(char_t* s)
+ {
+ assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
+ s++;
+
+ while (*s)
+ {
+ if (s[0] == '<' && s[1] == '!' && s[2] == '[')
+ {
+ // nested ignore section
+ s = parse_doctype_ignore(s);
+ if (!s) return s;
+ }
+ else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
+ {
+ // ignore section end
+ s += 3;
+
+ return s;
+ }
+ else s++;
+ }
+
+ PUGI__THROW_ERROR(status_bad_doctype, s);
+ }
+
+ char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel)
+ {
+ assert(s[0] == '<' && s[1] == '!');
+ s++;
+
+ while (*s)
+ {
+ if (s[0] == '<' && s[1] == '!' && s[2] != '-')
+ {
+ if (s[2] == '[')
+ {
+ // ignore
+ s = parse_doctype_ignore(s);
+ if (!s) return s;
+ }
+ else
+ {
+ // some control group
+ s = parse_doctype_group(s, endch, false);
+ if (!s) return s;
+ }
+ }
+ else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')
+ {
+ // unknown tag (forbidden), or some primitive group
+ s = parse_doctype_primitive(s);
+ if (!s) return s;
+ }
+ else if (*s == '>')
+ {
+ s++;
+
+ return s;
+ }
+ else s++;
+ }
+
+ if (!toplevel || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
+
+ return s;
+ }
+
+ char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)
+ {
+ // parse node contents, starting with exclamation mark
+ ++s;
+
+ if (*s == '-') // '<!-...'
+ {
+ ++s;
+
+ if (*s == '-') // '<!--...'
+ {
+ ++s;
+
+ if (PUGI__OPTSET(parse_comments))
+ {
+ PUGI__PUSHNODE(node_comment); // Append a new node on the tree.
+ cursor->value = s; // Save the offset.
+ }
+
+ if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))
+ {
+ s = strconv_comment(s, endch);
+
+ if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);
+ }
+ else
+ {
+ // Scan for terminating '-->'.
+ PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>'));
+ PUGI__CHECK_ERROR(status_bad_comment, s);
+
+ if (PUGI__OPTSET(parse_comments))
+ *s = 0; // Zero-terminate this segment at the first terminating '-'.
+
+ s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.
+ }
+ }
+ else PUGI__THROW_ERROR(status_bad_comment, s);
+ }
+ else if (*s == '[')
+ {
+ // '<![CDATA[...'
+ if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')
+ {
+ ++s;
+
+ if (PUGI__OPTSET(parse_cdata))
+ {
+ PUGI__PUSHNODE(node_cdata); // Append a new node on the tree.
+ cursor->value = s; // Save the offset.
+
+ if (PUGI__OPTSET(parse_eol))
+ {
+ s = strconv_cdata(s, endch);
+
+ if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);
+ }
+ else
+ {
+ // Scan for terminating ']]>'.
+ PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
+ PUGI__CHECK_ERROR(status_bad_cdata, s);
+
+ *s++ = 0; // Zero-terminate this segment.
+ }
+ }
+ else // Flagged for discard, but we still have to scan for the terminator.
+ {
+ // Scan for terminating ']]>'.
+ PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>'));
+ PUGI__CHECK_ERROR(status_bad_cdata, s);
+
+ ++s;
+ }
+
+ s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.
+ }
+ else PUGI__THROW_ERROR(status_bad_cdata, s);
+ }
+ else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && ENDSWITH(s[6], 'E'))
+ {
+ s -= 2;
+
+ if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+ char_t* mark = s + 9;
+
+ s = parse_doctype_group(s, endch, true);
+ if (!s) return s;
+
+ if (PUGI__OPTSET(parse_doctype))
+ {
+ while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark;
+
+ PUGI__PUSHNODE(node_doctype);
+
+ cursor->value = mark;
+
+ assert((s[0] == 0 && endch == '>') || s[-1] == '>');
+ s[*s == 0 ? 0 : -1] = 0;
+
+ PUGI__POPNODE();
+ }
+ }
+ else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);
+ else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);
+ else PUGI__THROW_ERROR(status_unrecognized_tag, s);
+
+ return s;
+ }
+
+ char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)
+ {
+ // load into registers
+ xml_node_struct* cursor = ref_cursor;
+ char_t ch = 0;
+
+ // parse node contents, starting with question mark
+ ++s;
+
+ // read PI target
+ char_t* target = s;
+
+ if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);
+
+ PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol));
+ PUGI__CHECK_ERROR(status_bad_pi, s);
+
+ // determine node type; stricmp / strcasecmp is not portable
+ bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;
+
+ if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))
+ {
+ if (declaration)
+ {
+ // disallow non top-level declarations
+ if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);
+
+ PUGI__PUSHNODE(node_declaration);
+ }
+ else
+ {
+ PUGI__PUSHNODE(node_pi);
+ }
+
+ cursor->name = target;
+
+ PUGI__ENDSEG();
+
+ // parse value/attributes
+ if (ch == '?')
+ {
+ // empty node
+ if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
+ s += (*s == '>');
+
+ PUGI__POPNODE();
+ }
+ else if (PUGI__IS_CHARTYPE(ch, ct_space))
+ {
+ PUGI__SKIPWS();
+
+ // scan for tag end
+ char_t* value = s;
+
+ PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
+ PUGI__CHECK_ERROR(status_bad_pi, s);
+
+ if (declaration)
+ {
+ // replace ending ? with / so that 'element' terminates properly
+ *s = '/';
+
+ // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES
+ s = value;
+ }
+ else
+ {
+ // store value and step over >
+ cursor->value = value;
+ PUGI__POPNODE();
+
+ PUGI__ENDSEG();
+
+ s += (*s == '>');
+ }
+ }
+ else PUGI__THROW_ERROR(status_bad_pi, s);
+ }
+ else
+ {
+ // scan for tag end
+ PUGI__SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>'));
+ PUGI__CHECK_ERROR(status_bad_pi, s);
+
+ s += (s[1] == '>' ? 2 : 1);
+ }
+
+ // store from registers
+ ref_cursor = cursor;
+
+ return s;
+ }
+
+ char_t* parse(char_t* s, xml_node_struct* xmldoc, unsigned int optmsk, char_t endch)
+ {
+ strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);
+ strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk);
+
+ char_t ch = 0;
+ xml_node_struct* cursor = xmldoc;
+ char_t* mark = s;
+
+ while (*s != 0)
+ {
+ if (*s == '<')
+ {
+ ++s;
+
+ LOC_TAG:
+ if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'
+ {
+ PUGI__PUSHNODE(node_element); // Append a new node to the tree.
+
+ cursor->name = s;
+
+ PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
+ PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
+
+ if (ch == '>')
+ {
+ // end of tag
+ }
+ else if (PUGI__IS_CHARTYPE(ch, ct_space))
+ {
+ LOC_ATTRIBUTES:
+ while (true)
+ {
+ PUGI__SKIPWS(); // Eat any whitespace.
+
+ if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
+ {
+ xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute.
+ if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
+
+ a->name = s; // Save the offset.
+
+ PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator.
+ PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
+
+ PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
+ PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
+
+ if (PUGI__IS_CHARTYPE(ch, ct_space))
+ {
+ PUGI__SKIPWS(); // Eat any whitespace.
+ PUGI__CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance
+
+ ch = *s;
+ ++s;
+ }
+
+ if (ch == '=') // '<... #=...'
+ {
+ PUGI__SKIPWS(); // Eat any whitespace.
+
+ if (*s == '"' || *s == '\'') // '<... #="...'
+ {
+ ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
+ ++s; // Step over the quote.
+ a->value = s; // Save the offset.
+
+ s = strconv_attribute(s, ch);
+
+ if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);
+
+ // After this line the loop continues from the start;
+ // Whitespaces, / and > are ok, symbols and EOF are wrong,
+ // everything else will be detected
+ if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);
+ }
+ else PUGI__THROW_ERROR(status_bad_attribute, s);
+ }
+ else PUGI__THROW_ERROR(status_bad_attribute, s);
+ }
+ else if (*s == '/')
+ {
+ ++s;
+
+ if (*s == '>')
+ {
+ PUGI__POPNODE();
+ s++;
+ break;
+ }
+ else if (*s == 0 && endch == '>')
+ {
+ PUGI__POPNODE();
+ break;
+ }
+ else PUGI__THROW_ERROR(status_bad_start_element, s);
+ }
+ else if (*s == '>')
+ {
+ ++s;
+
+ break;
+ }
+ else if (*s == 0 && endch == '>')
+ {
+ break;
+ }
+ else PUGI__THROW_ERROR(status_bad_start_element, s);
+ }
+
+ // !!!
+ }
+ else if (ch == '/') // '<#.../'
+ {
+ if (!ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
+
+ PUGI__POPNODE(); // Pop.
+
+ s += (*s == '>');
+ }
+ else if (ch == 0)
+ {
+ // we stepped over null terminator, backtrack & handle closing tag
+ --s;
+
+ if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);
+ }
+ else PUGI__THROW_ERROR(status_bad_start_element, s);
+ }
+ else if (*s == '/')
+ {
+ ++s;
+
+ char_t* name = cursor->name;
+ if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+
+ while (PUGI__IS_CHARTYPE(*s, ct_symbol))
+ {
+ if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ }
+
+ if (*name)
+ {
+ if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
+ else PUGI__THROW_ERROR(status_end_element_mismatch, s);
+ }
+
+ PUGI__POPNODE(); // Pop.
+
+ PUGI__SKIPWS();
+
+ if (*s == 0)
+ {
+ if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
+ }
+ else
+ {
+ if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
+ ++s;
+ }
+ }
+ else if (*s == '?') // '<?...'
+ {
+ s = parse_question(s, cursor, optmsk, endch);
+ if (!s) return s;
+
+ assert(cursor);
+ if ((cursor->header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES;
+ }
+ else if (*s == '!') // '<!...'
+ {
+ s = parse_exclamation(s, cursor, optmsk, endch);
+ if (!s) return s;
+ }
+ else if (*s == 0 && endch == '?') PUGI__THROW_ERROR(status_bad_pi, s);
+ else PUGI__THROW_ERROR(status_unrecognized_tag, s);
+ }
+ else
+ {
+ mark = s; // Save this offset while searching for a terminator.
+
+ PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
+
+ if (*s == '<')
+ {
+ // We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one
+ assert(mark != s);
+
+ if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single))
+ {
+ continue;
+ }
+ else if (PUGI__OPTSET(parse_ws_pcdata_single))
+ {
+ if (s[1] != '/' || cursor->first_child) continue;
+ }
+ }
+
+ s = mark;
+
+ if (cursor->parent)
+ {
+ PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
+ cursor->value = s; // Save the offset.
+
+ s = strconv_pcdata(s);
+
+ PUGI__POPNODE(); // Pop since this is a standalone.
+
+ if (!*s) break;
+ }
+ else
+ {
+ PUGI__SCANFOR(*s == '<'); // '...<'
+ if (!*s) break;
+
+ ++s;
+ }
+
+ // We're after '<'
+ goto LOC_TAG;
+ }
+ }
+
+ // check that last tag is closed
+ if (cursor != xmldoc) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+
+ return s;
+ }
+
+ static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk)
+ {
+ xml_document_struct* xmldoc = static_cast<xml_document_struct*>(root);
+
+ // store buffer for offset_debug
+ xmldoc->buffer = buffer;
+
+ // early-out for empty documents
+ if (length == 0) return make_parse_result(status_ok);
+
+ // create parser on stack
+ xml_parser parser(*xmldoc);
+
+ // save last character and make buffer zero-terminated (speeds up parsing)
+ char_t endch = buffer[length - 1];
+ buffer[length - 1] = 0;
+
+ // perform actual parsing
+ parser.parse(buffer, xmldoc, optmsk, endch);
+
+ xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
+ assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
+
+ // update allocator state
+ *static_cast<xml_allocator*>(xmldoc) = parser.alloc;
+
+ // since we removed last character, we have to handle the only possible false positive
+ if (result && endch == '<')
+ {
+ // there's no possible well-formed document with < at the end
+ return make_parse_result(status_unrecognized_tag, length);
+ }
+
+ return result;
+ }
+ };
+
+ // Output facilities
+ PUGI__FN xml_encoding get_write_native_encoding()
+ {
+ #ifdef PUGIXML_WCHAR_MODE
+ return get_wchar_encoding();
+ #else
+ return encoding_utf8;
+ #endif
+ }
+
+ PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)
+ {
+ // replace wchar encoding with utf implementation
+ if (encoding == encoding_wchar) return get_wchar_encoding();
+
+ // replace utf16 encoding with utf16 with specific endianness
+ if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+ // replace utf32 encoding with utf32 with specific endianness
+ if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+ // only do autodetection if no explicit encoding is requested
+ if (encoding != encoding_auto) return encoding;
+
+ // assume utf8 encoding
+ return encoding_utf8;
+ }
+
+#ifdef PUGIXML_WCHAR_MODE
+ PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
+ {
+ assert(length > 0);
+
+ // discard last character if it's the lead of a surrogate pair
+ return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;
+ }
+
+ PUGI__FN size_t convert_buffer(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
+ {
+ // only endian-swapping is required
+ if (need_endian_swap_utf(encoding, get_wchar_encoding()))
+ {
+ convert_wchar_endian_swap(r_char, data, length);
+
+ return length * sizeof(char_t);
+ }
+
+ // convert to utf8
+ if (encoding == encoding_utf8)
+ {
+ uint8_t* dest = r_u8;
+ uint8_t* end = utf_decoder<utf8_writer>::decode_wchar_block(data, length, dest);
+
+ return static_cast<size_t>(end - dest);
+ }
+
+ // convert to utf16
+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+ {
+ uint16_t* dest = r_u16;
+
+ // convert to native utf16
+ uint16_t* end = utf_decoder<utf16_writer>::decode_wchar_block(data, length, dest);
+
+ // swap if necessary
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
+
+ return static_cast<size_t>(end - dest) * sizeof(uint16_t);
+ }
+
+ // convert to utf32
+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+ {
+ uint32_t* dest = r_u32;
+
+ // convert to native utf32
+ uint32_t* end = utf_decoder<utf32_writer>::decode_wchar_block(data, length, dest);
+
+ // swap if necessary
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
+
+ return static_cast<size_t>(end - dest) * sizeof(uint32_t);
+ }
+
+ // convert to latin1
+ if (encoding == encoding_latin1)
+ {
+ uint8_t* dest = r_u8;
+ uint8_t* end = utf_decoder<latin1_writer>::decode_wchar_block(data, length, dest);
+
+ return static_cast<size_t>(end - dest);
+ }
+
+ assert(!"Invalid encoding");
+ return 0;
+ }
+#else
+ PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
+ {
+ assert(length > 4);
+
+ for (size_t i = 1; i <= 4; ++i)
+ {
+ uint8_t ch = static_cast<uint8_t>(data[length - i]);
+
+ // either a standalone character or a leading one
+ if ((ch & 0xc0) != 0x80) return length - i;
+ }
+
+ // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk
+ return length;
+ }
+
+ PUGI__FN size_t convert_buffer(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
+ {
+ if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+ {
+ uint16_t* dest = r_u16;
+
+ // convert to native utf16
+ uint16_t* end = utf_decoder<utf16_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
+
+ // swap if necessary
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
+
+ return static_cast<size_t>(end - dest) * sizeof(uint16_t);
+ }
+
+ if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+ {
+ uint32_t* dest = r_u32;
+
+ // convert to native utf32
+ uint32_t* end = utf_decoder<utf32_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
+
+ // swap if necessary
+ xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+ if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast<size_t>(end - dest));
+
+ return static_cast<size_t>(end - dest) * sizeof(uint32_t);
+ }
+
+ if (encoding == encoding_latin1)
+ {
+ uint8_t* dest = r_u8;
+ uint8_t* end = utf_decoder<latin1_writer>::decode_utf8_block(reinterpret_cast<const uint8_t*>(data), length, dest);
+
+ return static_cast<size_t>(end - dest);
+ }
+
+ assert(!"Invalid encoding");
+ return 0;
+ }
+#endif
+
+ class xml_buffered_writer
+ {
+ xml_buffered_writer(const xml_buffered_writer&);
+ xml_buffered_writer& operator=(const xml_buffered_writer&);
+
+ public:
+ xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))
+ {
+ PUGI__STATIC_ASSERT(bufcapacity >= 8);
+ }
+
+ ~xml_buffered_writer()
+ {
+ flush();
+ }
+
+ void flush()
+ {
+ flush(buffer, bufsize);
+ bufsize = 0;
+ }
+
+ void flush(const char_t* data, size_t size)
+ {
+ if (size == 0) return;
+
+ // fast path, just write data
+ if (encoding == get_write_native_encoding())
+ writer.write(data, size * sizeof(char_t));
+ else
+ {
+ // convert chunk
+ size_t result = convert_buffer(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
+ assert(result <= sizeof(scratch));
+
+ // write data
+ writer.write(scratch.data_u8, result);
+ }
+ }
+
+ void write(const char_t* data, size_t length)
+ {
+ if (bufsize + length > bufcapacity)
+ {
+ // flush the remaining buffer contents
+ flush();
+
+ // handle large chunks
+ if (length > bufcapacity)
+ {
+ if (encoding == get_write_native_encoding())
+ {
+ // fast path, can just write data chunk
+ writer.write(data, length * sizeof(char_t));
+ return;
+ }
+
+ // need to convert in suitable chunks
+ while (length > bufcapacity)
+ {
+ // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
+ // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
+ size_t chunk_size = get_valid_length(data, bufcapacity);
+
+ // convert chunk and write
+ flush(data, chunk_size);
+
+ // iterate
+ data += chunk_size;
+ length -= chunk_size;
+ }
+
+ // small tail is copied below
+ bufsize = 0;
+ }
+ }
+
+ memcpy(buffer + bufsize, data, length * sizeof(char_t));
+ bufsize += length;
+ }
+
+ void write(const char_t* data)
+ {
+ write(data, strlength(data));
+ }
+
+ void write(char_t d0)
+ {
+ if (bufsize + 1 > bufcapacity) flush();
+
+ buffer[bufsize + 0] = d0;
+ bufsize += 1;
+ }
+
+ void write(char_t d0, char_t d1)
+ {
+ if (bufsize + 2 > bufcapacity) flush();
+
+ buffer[bufsize + 0] = d0;
+ buffer[bufsize + 1] = d1;
+ bufsize += 2;
+ }
+
+ void write(char_t d0, char_t d1, char_t d2)
+ {
+ if (bufsize + 3 > bufcapacity) flush();
+
+ buffer[bufsize + 0] = d0;
+ buffer[bufsize + 1] = d1;
+ buffer[bufsize + 2] = d2;
+ bufsize += 3;
+ }
+
+ void write(char_t d0, char_t d1, char_t d2, char_t d3)
+ {
+ if (bufsize + 4 > bufcapacity) flush();
+
+ buffer[bufsize + 0] = d0;
+ buffer[bufsize + 1] = d1;
+ buffer[bufsize + 2] = d2;
+ buffer[bufsize + 3] = d3;
+ bufsize += 4;
+ }
+
+ void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
+ {
+ if (bufsize + 5 > bufcapacity) flush();
+
+ buffer[bufsize + 0] = d0;
+ buffer[bufsize + 1] = d1;
+ buffer[bufsize + 2] = d2;
+ buffer[bufsize + 3] = d3;
+ buffer[bufsize + 4] = d4;
+ bufsize += 5;
+ }
+
+ void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
+ {
+ if (bufsize + 6 > bufcapacity) flush();
+
+ buffer[bufsize + 0] = d0;
+ buffer[bufsize + 1] = d1;
+ buffer[bufsize + 2] = d2;
+ buffer[bufsize + 3] = d3;
+ buffer[bufsize + 4] = d4;
+ buffer[bufsize + 5] = d5;
+ bufsize += 6;
+ }
+
+ // utf8 maximum expansion: x4 (-> utf32)
+ // utf16 maximum expansion: x2 (-> utf32)
+ // utf32 maximum expansion: x1
+ enum
+ {
+ bufcapacitybytes =
+ #ifdef PUGIXML_MEMORY_OUTPUT_STACK
+ PUGIXML_MEMORY_OUTPUT_STACK
+ #else
+ 10240
+ #endif
+ ,
+ bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)
+ };
+
+ char_t buffer[bufcapacity];
+
+ union
+ {
+ uint8_t data_u8[4 * bufcapacity];
+ uint16_t data_u16[2 * bufcapacity];
+ uint32_t data_u32[bufcapacity];
+ char_t data_char[bufcapacity];
+ } scratch;
+
+ xml_writer& writer;
+ size_t bufsize;
+ xml_encoding encoding;
+ };
+
+ PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type)
+ {
+ while (*s)
+ {
+ const char_t* prev = s;
+
+ // While *s is a usual symbol
+ while (!PUGI__IS_CHARTYPEX(*s, type)) ++s;
+
+ writer.write(prev, static_cast<size_t>(s - prev));
+
+ switch (*s)
+ {
+ case 0: break;
+ case '&':
+ writer.write('&', 'a', 'm', 'p', ';');
+ ++s;
+ break;
+ case '<':
+ writer.write('&', 'l', 't', ';');
+ ++s;
+ break;
+ case '>':
+ writer.write('&', 'g', 't', ';');
+ ++s;
+ break;
+ case '"':
+ writer.write('&', 'q', 'u', 'o', 't', ';');
+ ++s;
+ break;
+ default: // s is not a usual symbol
+ {
+ unsigned int ch = static_cast<unsigned int>(*s++);
+ assert(ch < 32);
+
+ writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');
+ }
+ }
+ }
+ }
+
+ PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
+ {
+ if (flags & format_no_escapes)
+ writer.write(s);
+ else
+ text_output_escaped(writer, s, type);
+ }
+
+ PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
+ {
+ do
+ {
+ writer.write('<', '!', '[', 'C', 'D');
+ writer.write('A', 'T', 'A', '[');
+
+ const char_t* prev = s;
+
+ // look for ]]> sequence - we can't output it as is since it terminates CDATA
+ while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;
+
+ // skip ]] if we stopped at ]]>, > will go to the next CDATA section
+ if (*s) s += 2;
+
+ writer.write(prev, static_cast<size_t>(s - prev));
+
+ writer.write(']', ']', '>');
+ }
+ while (*s);
+ }
+
+ PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node& node, unsigned int flags)
+ {
+ const char_t* default_name = PUGIXML_TEXT(":anonymous");
+
+ for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute())
+ {
+ writer.write(' ');
+ writer.write(a.name()[0] ? a.name() : default_name);
+ writer.write('=', '"');
+
+ text_output(writer, a.value(), ctx_special_attr, flags);
+
+ writer.write('"');
+ }
+ }
+
+ PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth)
+ {
+ const char_t* default_name = PUGIXML_TEXT(":anonymous");
+
+ if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
+ for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
+
+ switch (node.type())
+ {
+ case node_document:
+ {
+ for (xml_node n = node.first_child(); n; n = n.next_sibling())
+ node_output(writer, n, indent, flags, depth);
+ break;
+ }
+
+ case node_element:
+ {
+ const char_t* name = node.name()[0] ? node.name() : default_name;
+
+ writer.write('<');
+ writer.write(name);
+
+ node_output_attributes(writer, node, flags);
+
+ if (flags & format_raw)
+ {
+ if (!node.first_child())
+ writer.write(' ', '/', '>');
+ else
+ {
+ writer.write('>');
+
+ for (xml_node n = node.first_child(); n; n = n.next_sibling())
+ node_output(writer, n, indent, flags, depth + 1);
+
+ writer.write('<', '/');
+ writer.write(name);
+ writer.write('>');
+ }
+ }
+ else if (!node.first_child())
+ writer.write(' ', '/', '>', '\n');
+ else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata))
+ {
+ writer.write('>');
+
+ if (node.first_child().type() == node_pcdata)
+ text_output(writer, node.first_child().value(), ctx_special_pcdata, flags);
+ else
+ text_output_cdata(writer, node.first_child().value());
+
+ writer.write('<', '/');
+ writer.write(name);
+ writer.write('>', '\n');
+ }
+ else
+ {
+ writer.write('>', '\n');
+
+ for (xml_node n = node.first_child(); n; n = n.next_sibling())
+ node_output(writer, n, indent, flags, depth + 1);
+
+ if ((flags & format_indent) != 0 && (flags & format_raw) == 0)
+ for (unsigned int i = 0; i < depth; ++i) writer.write(indent);
+
+ writer.write('<', '/');
+ writer.write(name);
+ writer.write('>', '\n');
+ }
+
+ break;
+ }
+
+ case node_pcdata:
+ text_output(writer, node.value(), ctx_special_pcdata, flags);
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_cdata:
+ text_output_cdata(writer, node.value());
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_comment:
+ writer.write('<', '!', '-', '-');
+ writer.write(node.value());
+ writer.write('-', '-', '>');
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_pi:
+ case node_declaration:
+ writer.write('<', '?');
+ writer.write(node.name()[0] ? node.name() : default_name);
+
+ if (node.type() == node_declaration)
+ {
+ node_output_attributes(writer, node, flags);
+ }
+ else if (node.value()[0])
+ {
+ writer.write(' ');
+ writer.write(node.value());
+ }
+
+ writer.write('?', '>');
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ case node_doctype:
+ writer.write('<', '!', 'D', 'O', 'C');
+ writer.write('T', 'Y', 'P', 'E');
+
+ if (node.value()[0])
+ {
+ writer.write(' ');
+ writer.write(node.value());
+ }
+
+ writer.write('>');
+ if ((flags & format_raw) == 0) writer.write('\n');
+ break;
+
+ default:
+ assert(!"Invalid node type");
+ }
+ }
+
+ inline bool has_declaration(const xml_node& node)
+ {
+ for (xml_node child = node.first_child(); child; child = child.next_sibling())
+ {
+ xml_node_type type = child.type();
+
+ if (type == node_declaration) return true;
+ if (type == node_element) return false;
+ }
+
+ return false;
+ }
+
+ inline bool allow_insert_child(xml_node_type parent, xml_node_type child)
+ {
+ if (parent != node_document && parent != node_element) return false;
+ if (child == node_document || child == node_null) return false;
+ if (parent != node_document && (child == node_declaration || child == node_doctype)) return false;
+
+ return true;
+ }
+
+ PUGI__FN void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip)
+ {
+ assert(dest.type() == source.type());
+
+ switch (source.type())
+ {
+ case node_element:
+ {
+ dest.set_name(source.name());
+
+ for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
+ dest.append_attribute(a.name()).set_value(a.value());
+
+ for (xml_node c = source.first_child(); c; c = c.next_sibling())
+ {
+ if (c == skip) continue;
+
+ xml_node cc = dest.append_child(c.type());
+ assert(cc);
+
+ recursive_copy_skip(cc, c, skip);
+ }
+
+ break;
+ }
+
+ case node_pcdata:
+ case node_cdata:
+ case node_comment:
+ case node_doctype:
+ dest.set_value(source.value());
+ break;
+
+ case node_pi:
+ dest.set_name(source.name());
+ dest.set_value(source.value());
+ break;
+
+ case node_declaration:
+ {
+ dest.set_name(source.name());
+
+ for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute())
+ dest.append_attribute(a.name()).set_value(a.value());
+
+ break;
+ }
+
+ default:
+ assert(!"Invalid node type");
+ }
+ }
+
+ inline bool is_text_node(xml_node_struct* node)
+ {
+ xml_node_type type = static_cast<xml_node_type>((node->header & impl::xml_memory_page_type_mask) + 1);
+
+ return type == node_pcdata || type == node_cdata;
+ }
+
+ // get value with conversion functions
+ PUGI__FN int get_value_int(const char_t* value, int def)
+ {
+ if (!value) return def;
+
+ #ifdef PUGIXML_WCHAR_MODE
+ return static_cast<int>(wcstol(value, 0, 10));
+ #else
+ return static_cast<int>(strtol(value, 0, 10));
+ #endif
+ }
+
+ PUGI__FN unsigned int get_value_uint(const char_t* value, unsigned int def)
+ {
+ if (!value) return def;
+
+ #ifdef PUGIXML_WCHAR_MODE
+ return static_cast<unsigned int>(wcstoul(value, 0, 10));
+ #else
+ return static_cast<unsigned int>(strtoul(value, 0, 10));
+ #endif
+ }
+
+ PUGI__FN double get_value_double(const char_t* value, double def)
+ {
+ if (!value) return def;
+
+ #ifdef PUGIXML_WCHAR_MODE
+ return wcstod(value, 0);
+ #else
+ return strtod(value, 0);
+ #endif
+ }
+
+ PUGI__FN float get_value_float(const char_t* value, float def)
+ {
+ if (!value) return def;
+
+ #ifdef PUGIXML_WCHAR_MODE
+ return static_cast<float>(wcstod(value, 0));
+ #else
+ return static_cast<float>(strtod(value, 0));
+ #endif
+ }
+
+ PUGI__FN bool get_value_bool(const char_t* value, bool def)
+ {
+ if (!value) return def;
+
+ // only look at first char
+ char_t first = *value;
+
+ // 1*, t* (true), T* (True), y* (yes), Y* (YES)
+ return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');
+ }
+
+ // set value with conversion functions
+ PUGI__FN bool set_value_buffer(char_t*& dest, uintptr_t& header, uintptr_t header_mask, char (&buf)[128])
+ {
+ #ifdef PUGIXML_WCHAR_MODE
+ char_t wbuf[128];
+ impl::widen_ascii(wbuf, buf);
+
+ return strcpy_insitu(dest, header, header_mask, wbuf);
+ #else
+ return strcpy_insitu(dest, header, header_mask, buf);
+ #endif
+ }
+
+ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value)
+ {
+ char buf[128];
+ sprintf(buf, "%d", value);
+
+ return set_value_buffer(dest, header, header_mask, buf);
+ }
+
+ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, unsigned int value)
+ {
+ char buf[128];
+ sprintf(buf, "%u", value);
+
+ return set_value_buffer(dest, header, header_mask, buf);
+ }
+
+ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, double value)
+ {
+ char buf[128];
+ sprintf(buf, "%g", value);
+
+ return set_value_buffer(dest, header, header_mask, buf);
+ }
+
+ PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, bool value)
+ {
+ return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
+ }
+
+ // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick
+ PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
+ {
+ #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
+ // there are 64-bit versions of fseek/ftell, let's use them
+ typedef __int64 length_type;
+
+ _fseeki64(file, 0, SEEK_END);
+ length_type length = _ftelli64(file);
+ _fseeki64(file, 0, SEEK_SET);
+ #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && !defined(__STRICT_ANSI__)
+ // there are 64-bit versions of fseek/ftell, let's use them
+ typedef off64_t length_type;
+
+ fseeko64(file, 0, SEEK_END);
+ length_type length = ftello64(file);
+ fseeko64(file, 0, SEEK_SET);
+ #else
+ // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway.
+ typedef long length_type;
+
+ fseek(file, 0, SEEK_END);
+ length_type length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ #endif
+
+ // check for I/O errors
+ if (length < 0) return status_io_error;
+
+ // check for overflow
+ size_t result = static_cast<size_t>(length);
+
+ if (static_cast<length_type>(result) != length) return status_out_of_memory;
+
+ // finalize
+ out_result = result;
+
+ return status_ok;
+ }
+
+ PUGI__FN xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding)
+ {
+ if (!file) return make_parse_result(status_file_not_found);
+
+ // get file size (can result in I/O errors)
+ size_t size = 0;
+ xml_parse_status size_status = get_file_size(file, size);
+
+ if (size_status != status_ok)
+ {
+ fclose(file);
+ return make_parse_result(size_status);
+ }
+
+ // allocate buffer for the whole file
+ char* contents = static_cast<char*>(xml_memory::allocate(size > 0 ? size : 1));
+
+ if (!contents)
+ {
+ fclose(file);
+ return make_parse_result(status_out_of_memory);
+ }
+
+ // read file in memory
+ size_t read_size = fread(contents, 1, size, file);
+ fclose(file);
+
+ if (read_size != size)
+ {
+ xml_memory::deallocate(contents);
+ return make_parse_result(status_io_error);
+ }
+
+ return doc.load_buffer_inplace_own(contents, size, options, encoding);
+ }
+
+#ifndef PUGIXML_NO_STL
+ template <typename T> struct xml_stream_chunk
+ {
+ static xml_stream_chunk* create()
+ {
+ void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
+
+ return new (memory) xml_stream_chunk();
+ }
+
+ static void destroy(void* ptr)
+ {
+ xml_stream_chunk* chunk = static_cast<xml_stream_chunk*>(ptr);
+
+ // free chunk chain
+ while (chunk)
+ {
+ xml_stream_chunk* next = chunk->next;
+ xml_memory::deallocate(chunk);
+ chunk = next;
+ }
+ }
+
+ xml_stream_chunk(): next(0), size(0)
+ {
+ }
+
+ xml_stream_chunk* next;
+ size_t size;
+
+ T data[xml_memory_page_size / sizeof(T)];
+ };
+
+ template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
+ {
+ buffer_holder chunks(0, xml_stream_chunk<T>::destroy);
+
+ // read file to a chunk list
+ size_t total = 0;
+ xml_stream_chunk<T>* last = 0;
+
+ while (!stream.eof())
+ {
+ // allocate new chunk
+ xml_stream_chunk<T>* chunk = xml_stream_chunk<T>::create();
+ if (!chunk) return status_out_of_memory;
+
+ // append chunk to list
+ if (last) last = last->next = chunk;
+ else chunks.data = last = chunk;
+
+ // read data to chunk
+ stream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T)));
+ chunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T);
+
+ // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors
+ if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
+
+ // guard against huge files (chunk size is small enough to make this overflow check work)
+ if (total + chunk->size < total) return status_out_of_memory;
+ total += chunk->size;
+ }
+
+ // copy chunk list to a contiguous buffer
+ char* buffer = static_cast<char*>(xml_memory::allocate(total));
+ if (!buffer) return status_out_of_memory;
+
+ char* write = buffer;
+
+ for (xml_stream_chunk<T>* chunk = static_cast<xml_stream_chunk<T>*>(chunks.data); chunk; chunk = chunk->next)
+ {
+ assert(write + chunk->size <= buffer + total);
+ memcpy(write, chunk->data, chunk->size);
+ write += chunk->size;
+ }
+
+ assert(write == buffer + total);
+
+ // return buffer
+ *out_buffer = buffer;
+ *out_size = total;
+
+ return status_ok;
+ }
+
+ template <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
+ {
+ // get length of remaining data in stream
+ typename std::basic_istream<T>::pos_type pos = stream.tellg();
+ stream.seekg(0, std::ios::end);
+ std::streamoff length = stream.tellg() - pos;
+ stream.seekg(pos);
+
+ if (stream.fail() || pos < 0) return status_io_error;
+
+ // guard against huge files
+ size_t read_length = static_cast<size_t>(length);
+
+ if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;
+
+ // read stream data into memory (guard against stream exceptions with buffer holder)
+ buffer_holder buffer(xml_memory::allocate((read_length > 0 ? read_length : 1) * sizeof(T)), xml_memory::deallocate);
+ if (!buffer.data) return status_out_of_memory;
+
+ stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));
+
+ // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors
+ if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
+
+ // return buffer
+ size_t actual_length = static_cast<size_t>(stream.gcount());
+ assert(actual_length <= read_length);
+
+ *out_buffer = buffer.release();
+ *out_size = actual_length * sizeof(T);
+
+ return status_ok;
+ }
+
+ template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding)
+ {
+ void* buffer = 0;
+ size_t size = 0;
+
+ // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
+ xml_parse_status status = (stream.tellg() < 0) ? load_stream_data_noseek(stream, &buffer, &size) : load_stream_data_seek(stream, &buffer, &size);
+ if (status != status_ok) return make_parse_result(status);
+
+ return doc.load_buffer_inplace_own(buffer, size, options, encoding);
+ }
+#endif
+
+#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && !defined(__STRICT_ANSI__))
+ PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
+ {
+ return _wfopen(path, mode);
+ }
+#else
+ PUGI__FN char* convert_path_heap(const wchar_t* str)
+ {
+ assert(str);
+
+ // first pass: get length in utf8 characters
+ size_t length = wcslen(str);
+ size_t size = as_utf8_begin(str, length);
+
+ // allocate resulting string
+ char* result = static_cast<char*>(xml_memory::allocate(size + 1));
+ if (!result) return 0;
+
+ // second pass: convert to utf8
+ as_utf8_end(result, size, str, length);
+
+ return result;
+ }
+
+ PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
+ {
+ // there is no standard function to open wide paths, so our best bet is to try utf8 path
+ char* path_utf8 = convert_path_heap(path);
+ if (!path_utf8) return 0;
+
+ // convert mode to ASCII (we mirror _wfopen interface)
+ char mode_ascii[4] = {0};
+ for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);
+
+ // try to open the utf8 path
+ FILE* result = fopen(path_utf8, mode_ascii);
+
+ // free dummy buffer
+ xml_memory::deallocate(path_utf8);
+
+ return result;
+ }
+#endif
+
+ PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
+ {
+ if (!file) return false;
+
+ xml_writer_file writer(file);
+ doc.save(writer, indent, flags, encoding);
+
+ int result = ferror(file);
+
+ fclose(file);
+
+ return result == 0;
+ }
+PUGI__NS_END
+
+namespace pugi
+{
+ PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_)
+ {
+ }
+
+ PUGI__FN void xml_writer_file::write(const void* data, size_t size)
+ {
+ size_t result = fwrite(data, 1, size, static_cast<FILE*>(file));
+ (void)!result; // unfortunately we can't do proper error handling here
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
+ {
+ }
+
+ PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)
+ {
+ }
+
+ PUGI__FN void xml_writer_stream::write(const void* data, size_t size)
+ {
+ if (narrow_stream)
+ {
+ assert(!wide_stream);
+ narrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));
+ }
+ else
+ {
+ assert(wide_stream);
+ assert(size % sizeof(wchar_t) == 0);
+
+ wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t)));
+ }
+ }
+#endif
+
+ PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0)
+ {
+ }
+
+ PUGI__FN xml_tree_walker::~xml_tree_walker()
+ {
+ }
+
+ PUGI__FN int xml_tree_walker::depth() const
+ {
+ return _depth;
+ }
+
+ PUGI__FN bool xml_tree_walker::begin(xml_node&)
+ {
+ return true;
+ }
+
+ PUGI__FN bool xml_tree_walker::end(xml_node&)
+ {
+ return true;
+ }
+
+ PUGI__FN xml_attribute::xml_attribute(): _attr(0)
+ {
+ }
+
+ PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)
+ {
+ }
+
+ PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***)
+ {
+ }
+
+ PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const
+ {
+ return _attr ? unspecified_bool_xml_attribute : 0;
+ }
+
+ PUGI__FN bool xml_attribute::operator!() const
+ {
+ return !_attr;
+ }
+
+ PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const
+ {
+ return (_attr == r._attr);
+ }
+
+ PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const
+ {
+ return (_attr != r._attr);
+ }
+
+ PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const
+ {
+ return (_attr < r._attr);
+ }
+
+ PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const
+ {
+ return (_attr > r._attr);
+ }
+
+ PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const
+ {
+ return (_attr <= r._attr);
+ }
+
+ PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const
+ {
+ return (_attr >= r._attr);
+ }
+
+ PUGI__FN xml_attribute xml_attribute::next_attribute() const
+ {
+ return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
+ }
+
+ PUGI__FN xml_attribute xml_attribute::previous_attribute() const
+ {
+ return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute();
+ }
+
+ PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
+ {
+ return (_attr && _attr->value) ? _attr->value : def;
+ }
+
+ PUGI__FN int xml_attribute::as_int(int def) const
+ {
+ return impl::get_value_int(_attr ? _attr->value : 0, def);
+ }
+
+ PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
+ {
+ return impl::get_value_uint(_attr ? _attr->value : 0, def);
+ }
+
+ PUGI__FN double xml_attribute::as_double(double def) const
+ {
+ return impl::get_value_double(_attr ? _attr->value : 0, def);
+ }
+
+ PUGI__FN float xml_attribute::as_float(float def) const
+ {
+ return impl::get_value_float(_attr ? _attr->value : 0, def);
+ }
+
+ PUGI__FN bool xml_attribute::as_bool(bool def) const
+ {
+ return impl::get_value_bool(_attr ? _attr->value : 0, def);
+ }
+
+ PUGI__FN bool xml_attribute::empty() const
+ {
+ return !_attr;
+ }
+
+ PUGI__FN const char_t* xml_attribute::name() const
+ {
+ return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT("");
+ }
+
+ PUGI__FN const char_t* xml_attribute::value() const
+ {
+ return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT("");
+ }
+
+ PUGI__FN size_t xml_attribute::hash_value() const
+ {
+ return static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));
+ }
+
+ PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const
+ {
+ return _attr;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute::operator=(int rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute::operator=(double rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
+ {
+ set_value(rhs);
+ return *this;
+ }
+
+ PUGI__FN bool xml_attribute::set_name(const char_t* rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(const char_t* rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(int rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(double rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ }
+
+ PUGI__FN bool xml_attribute::set_value(bool rhs)
+ {
+ if (!_attr) return false;
+
+ return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+ }
+
+#ifdef __BORLANDC__
+ PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs)
+ {
+ return (bool)lhs && rhs;
+ }
+
+ PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs)
+ {
+ return (bool)lhs || rhs;
+ }
+#endif
+
+ PUGI__FN xml_node::xml_node(): _root(0)
+ {
+ }
+
+ PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p)
+ {
+ }
+
+ PUGI__FN static void unspecified_bool_xml_node(xml_node***)
+ {
+ }
+
+ PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const
+ {
+ return _root ? unspecified_bool_xml_node : 0;
+ }
+
+ PUGI__FN bool xml_node::operator!() const
+ {
+ return !_root;
+ }
+
+ PUGI__FN xml_node::iterator xml_node::begin() const
+ {
+ return iterator(_root ? _root->first_child : 0, _root);
+ }
+
+ PUGI__FN xml_node::iterator xml_node::end() const
+ {
+ return iterator(0, _root);
+ }
+
+ PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
+ {
+ return attribute_iterator(_root ? _root->first_attribute : 0, _root);
+ }
+
+ PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const
+ {
+ return attribute_iterator(0, _root);
+ }
+
+ PUGI__FN xml_object_range<xml_node_iterator> xml_node::children() const
+ {
+ return xml_object_range<xml_node_iterator>(begin(), end());
+ }
+
+ PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
+ {
+ return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_), name_), xml_named_node_iterator());
+ }
+
+ PUGI__FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const
+ {
+ return xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end());
+ }
+
+ PUGI__FN bool xml_node::operator==(const xml_node& r) const
+ {
+ return (_root == r._root);
+ }
+
+ PUGI__FN bool xml_node::operator!=(const xml_node& r) const
+ {
+ return (_root != r._root);
+ }
+
+ PUGI__FN bool xml_node::operator<(const xml_node& r) const
+ {
+ return (_root < r._root);
+ }
+
+ PUGI__FN bool xml_node::operator>(const xml_node& r) const
+ {
+ return (_root > r._root);
+ }
+
+ PUGI__FN bool xml_node::operator<=(const xml_node& r) const
+ {
+ return (_root <= r._root);
+ }
+
+ PUGI__FN bool xml_node::operator>=(const xml_node& r) const
+ {
+ return (_root >= r._root);
+ }
+
+ PUGI__FN bool xml_node::empty() const
+ {
+ return !_root;
+ }
+
+ PUGI__FN const char_t* xml_node::name() const
+ {
+ return (_root && _root->name) ? _root->name : PUGIXML_TEXT("");
+ }
+
+ PUGI__FN xml_node_type xml_node::type() const
+ {
+ return _root ? static_cast<xml_node_type>((_root->header & impl::xml_memory_page_type_mask) + 1) : node_null;
+ }
+
+ PUGI__FN const char_t* xml_node::value() const
+ {
+ return (_root && _root->value) ? _root->value : PUGIXML_TEXT("");
+ }
+
+ PUGI__FN xml_node xml_node::child(const char_t* name_) const
+ {
+ if (!_root) return xml_node();
+
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const
+ {
+ if (!_root) return xml_attribute();
+
+ for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
+ if (i->name && impl::strequal(name_, i->name))
+ return xml_attribute(i);
+
+ return xml_attribute();
+ }
+
+ PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const
+ {
+ if (!_root) return xml_node();
+
+ for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
+ if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::next_sibling() const
+ {
+ if (!_root) return xml_node();
+
+ if (_root->next_sibling) return xml_node(_root->next_sibling);
+ else return xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const
+ {
+ if (!_root) return xml_node();
+
+ for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
+ if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+
+ return xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::previous_sibling() const
+ {
+ if (!_root) return xml_node();
+
+ if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);
+ else return xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::parent() const
+ {
+ return _root ? xml_node(_root->parent) : xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::root() const
+ {
+ if (!_root) return xml_node();
+
+ impl::xml_memory_page* page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
+
+ return xml_node(static_cast<impl::xml_document_struct*>(page->allocator));
+ }
+
+ PUGI__FN xml_text xml_node::text() const
+ {
+ return xml_text(_root);
+ }
+
+ PUGI__FN const char_t* xml_node::child_value() const
+ {
+ if (!_root) return PUGIXML_TEXT("");
+
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if (i->value && impl::is_text_node(i))
+ return i->value;
+
+ return PUGIXML_TEXT("");
+ }
+
+ PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const
+ {
+ return child(name_).child_value();
+ }
+
+ PUGI__FN xml_attribute xml_node::first_attribute() const
+ {
+ return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
+ }
+
+ PUGI__FN xml_attribute xml_node::last_attribute() const
+ {
+ return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute();
+ }
+
+ PUGI__FN xml_node xml_node::first_child() const
+ {
+ return _root ? xml_node(_root->first_child) : xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::last_child() const
+ {
+ return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node();
+ }
+
+ PUGI__FN bool xml_node::set_name(const char_t* rhs)
+ {
+ switch (type())
+ {
+ case node_pi:
+ case node_declaration:
+ case node_element:
+ return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs);
+
+ default:
+ return false;
+ }
+ }
+
+ PUGI__FN bool xml_node::set_value(const char_t* rhs)
+ {
+ switch (type())
+ {
+ case node_pi:
+ case node_cdata:
+ case node_pcdata:
+ case node_comment:
+ case node_doctype:
+ return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs);
+
+ default:
+ return false;
+ }
+ }
+
+ PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
+ {
+ if (type() != node_element && type() != node_declaration) return xml_attribute();
+
+ xml_attribute a(impl::append_attribute_ll(_root, impl::get_allocator(_root)));
+ a.set_name(name_);
+
+ return a;
+ }
+
+ PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
+ {
+ if (type() != node_element && type() != node_declaration) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ if (!a) return xml_attribute();
+
+ a.set_name(name_);
+
+ xml_attribute_struct* head = _root->first_attribute;
+
+ if (head)
+ {
+ a._attr->prev_attribute_c = head->prev_attribute_c;
+ head->prev_attribute_c = a._attr;
+ }
+ else
+ a._attr->prev_attribute_c = a._attr;
+
+ a._attr->next_attribute = head;
+ _root->first_attribute = a._attr;
+
+ return a;
+ }
+
+ PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
+ {
+ if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
+
+ // check that attribute belongs to *this
+ xml_attribute_struct* cur = attr._attr;
+
+ while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
+
+ if (cur != _root->first_attribute) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ if (!a) return xml_attribute();
+
+ a.set_name(name_);
+
+ if (attr._attr->prev_attribute_c->next_attribute)
+ attr._attr->prev_attribute_c->next_attribute = a._attr;
+ else
+ _root->first_attribute = a._attr;
+
+ a._attr->prev_attribute_c = attr._attr->prev_attribute_c;
+ a._attr->next_attribute = attr._attr;
+ attr._attr->prev_attribute_c = a._attr;
+
+ return a;
+ }
+
+ PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
+ {
+ if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute();
+
+ // check that attribute belongs to *this
+ xml_attribute_struct* cur = attr._attr;
+
+ while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c;
+
+ if (cur != _root->first_attribute) return xml_attribute();
+
+ xml_attribute a(impl::allocate_attribute(impl::get_allocator(_root)));
+ if (!a) return xml_attribute();
+
+ a.set_name(name_);
+
+ if (attr._attr->next_attribute)
+ attr._attr->next_attribute->prev_attribute_c = a._attr;
+ else
+ _root->first_attribute->prev_attribute_c = a._attr;
+
+ a._attr->next_attribute = attr._attr->next_attribute;
+ a._attr->prev_attribute_c = attr._attr;
+ attr._attr->next_attribute = a._attr;
+
+ return a;
+ }
+
+ PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
+ {
+ if (!proto) return xml_attribute();
+
+ xml_attribute result = append_attribute(proto.name());
+ result.set_value(proto.value());
+
+ return result;
+ }
+
+ PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
+ {
+ if (!proto) return xml_attribute();
+
+ xml_attribute result = prepend_attribute(proto.name());
+ result.set_value(proto.value());
+
+ return result;
+ }
+
+ PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
+ {
+ if (!proto) return xml_attribute();
+
+ xml_attribute result = insert_attribute_after(proto.name(), attr);
+ result.set_value(proto.value());
+
+ return result;
+ }
+
+ PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
+ {
+ if (!proto) return xml_attribute();
+
+ xml_attribute result = insert_attribute_before(proto.name(), attr);
+ result.set_value(proto.value());
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
+ {
+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
+
+ xml_node n(impl::append_node(_root, impl::get_allocator(_root), type_));
+
+ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+ return n;
+ }
+
+ PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
+ {
+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
+
+ xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ if (!n) return xml_node();
+
+ n._root->parent = _root;
+
+ xml_node_struct* head = _root->first_child;
+
+ if (head)
+ {
+ n._root->prev_sibling_c = head->prev_sibling_c;
+ head->prev_sibling_c = n._root;
+ }
+ else
+ n._root->prev_sibling_c = n._root;
+
+ n._root->next_sibling = head;
+ _root->first_child = n._root;
+
+ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+ return n;
+ }
+
+ PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)
+ {
+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
+ if (!node._root || node._root->parent != _root) return xml_node();
+
+ xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ if (!n) return xml_node();
+
+ n._root->parent = _root;
+
+ if (node._root->prev_sibling_c->next_sibling)
+ node._root->prev_sibling_c->next_sibling = n._root;
+ else
+ _root->first_child = n._root;
+
+ n._root->prev_sibling_c = node._root->prev_sibling_c;
+ n._root->next_sibling = node._root;
+ node._root->prev_sibling_c = n._root;
+
+ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+ return n;
+ }
+
+ PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
+ {
+ if (!impl::allow_insert_child(this->type(), type_)) return xml_node();
+ if (!node._root || node._root->parent != _root) return xml_node();
+
+ xml_node n(impl::allocate_node(impl::get_allocator(_root), type_));
+ if (!n) return xml_node();
+
+ n._root->parent = _root;
+
+ if (node._root->next_sibling)
+ node._root->next_sibling->prev_sibling_c = n._root;
+ else
+ _root->first_child->prev_sibling_c = n._root;
+
+ n._root->next_sibling = node._root->next_sibling;
+ n._root->prev_sibling_c = node._root;
+ node._root->next_sibling = n._root;
+
+ if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+ return n;
+ }
+
+ PUGI__FN xml_node xml_node::append_child(const char_t* name_)
+ {
+ xml_node result = append_child(node_element);
+
+ result.set_name(name_);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
+ {
+ xml_node result = prepend_child(node_element);
+
+ result.set_name(name_);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
+ {
+ xml_node result = insert_child_after(node_element, node);
+
+ result.set_name(name_);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
+ {
+ xml_node result = insert_child_before(node_element, node);
+
+ result.set_name(name_);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
+ {
+ xml_node result = append_child(proto.type());
+
+ if (result) impl::recursive_copy_skip(result, proto, result);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
+ {
+ xml_node result = prepend_child(proto.type());
+
+ if (result) impl::recursive_copy_skip(result, proto, result);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
+ {
+ xml_node result = insert_child_after(proto.type(), node);
+
+ if (result) impl::recursive_copy_skip(result, proto, result);
+
+ return result;
+ }
+
+ PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
+ {
+ xml_node result = insert_child_before(proto.type(), node);
+
+ if (result) impl::recursive_copy_skip(result, proto, result);
+
+ return result;
+ }
+
+ PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
+ {
+ return remove_attribute(attribute(name_));
+ }
+
+ PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)
+ {
+ if (!_root || !a._attr) return false;
+
+ // check that attribute belongs to *this
+ xml_attribute_struct* attr = a._attr;
+
+ while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c;
+
+ if (attr != _root->first_attribute) return false;
+
+ if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c;
+ else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c;
+
+ if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute;
+ else _root->first_attribute = a._attr->next_attribute;
+
+ impl::destroy_attribute(a._attr, impl::get_allocator(_root));
+
+ return true;
+ }
+
+ PUGI__FN bool xml_node::remove_child(const char_t* name_)
+ {
+ return remove_child(child(name_));
+ }
+
+ PUGI__FN bool xml_node::remove_child(const xml_node& n)
+ {
+ if (!_root || !n._root || n._root->parent != _root) return false;
+
+ if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c;
+ else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c;
+
+ if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling;
+ else _root->first_child = n._root->next_sibling;
+
+ impl::destroy_node(n._root, impl::get_allocator(_root));
+
+ return true;
+ }
+
+ PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
+ {
+ if (!_root) return xml_node();
+
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if (i->name && impl::strequal(name_, i->name))
+ {
+ for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
+ if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value))
+ return xml_node(i);
+ }
+
+ return xml_node();
+ }
+
+ PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const
+ {
+ if (!_root) return xml_node();
+
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
+ if (impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value))
+ return xml_node(i);
+
+ return xml_node();
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN string_t xml_node::path(char_t delimiter) const
+ {
+ xml_node cursor = *this; // Make a copy.
+
+ string_t result = cursor.name();
+
+ while (cursor.parent())
+ {
+ cursor = cursor.parent();
+
+ string_t temp = cursor.name();
+ temp += delimiter;
+ temp += result;
+ result.swap(temp);
+ }
+
+ return result;
+ }
+#endif
+
+ PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const
+ {
+ xml_node found = *this; // Current search context.
+
+ if (!_root || !path_ || !path_[0]) return found;
+
+ if (path_[0] == delimiter)
+ {
+ // Absolute path; e.g. '/foo/bar'
+ found = found.root();
+ ++path_;
+ }
+
+ const char_t* path_segment = path_;
+
+ while (*path_segment == delimiter) ++path_segment;
+
+ const char_t* path_segment_end = path_segment;
+
+ while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;
+
+ if (path_segment == path_segment_end) return found;
+
+ const char_t* next_segment = path_segment_end;
+
+ while (*next_segment == delimiter) ++next_segment;
+
+ if (*path_segment == '.' && path_segment + 1 == path_segment_end)
+ return found.first_element_by_path(next_segment, delimiter);
+ else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)
+ return found.parent().first_element_by_path(next_segment, delimiter);
+ else
+ {
+ for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling)
+ {
+ if (j->name && impl::strequalrange(j->name, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
+ {
+ xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
+
+ if (subsearch) return subsearch;
+ }
+ }
+
+ return xml_node();
+ }
+ }
+
+ PUGI__FN bool xml_node::traverse(xml_tree_walker& walker)
+ {
+ walker._depth = -1;
+
+ xml_node arg_begin = *this;
+ if (!walker.begin(arg_begin)) return false;
+
+ xml_node cur = first_child();
+
+ if (cur)
+ {
+ ++walker._depth;
+
+ do
+ {
+ xml_node arg_for_each = cur;
+ if (!walker.for_each(arg_for_each))
+ return false;
+
+ if (cur.first_child())
+ {
+ ++walker._depth;
+ cur = cur.first_child();
+ }
+ else if (cur.next_sibling())
+ cur = cur.next_sibling();
+ else
+ {
+ // Borland C++ workaround
+ while (!cur.next_sibling() && cur != *this && !cur.parent().empty())
+ {
+ --walker._depth;
+ cur = cur.parent();
+ }
+
+ if (cur != *this)
+ cur = cur.next_sibling();
+ }
+ }
+ while (cur && cur != *this);
+ }
+
+ assert(walker._depth == -1);
+
+ xml_node arg_end = *this;
+ return walker.end(arg_end);
+ }
+
+ PUGI__FN size_t xml_node::hash_value() const
+ {
+ return static_cast<size_t>(reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct));
+ }
+
+ PUGI__FN xml_node_struct* xml_node::internal_object() const
+ {
+ return _root;
+ }
+
+ PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
+ {
+ if (!_root) return;
+
+ impl::xml_buffered_writer buffered_writer(writer, encoding);
+
+ impl::node_output(buffered_writer, *this, indent, flags, depth);
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
+ {
+ xml_writer_stream writer(stream);
+
+ print(writer, indent, flags, encoding, depth);
+ }
+
+ PUGI__FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const
+ {
+ xml_writer_stream writer(stream);
+
+ print(writer, indent, flags, encoding_wchar, depth);
+ }
+#endif
+
+ PUGI__FN ptrdiff_t xml_node::offset_debug() const
+ {
+ xml_node_struct* r = root()._root;
+
+ if (!r) return -1;
+
+ const char_t* buffer = static_cast<impl::xml_document_struct*>(r)->buffer;
+
+ if (!buffer) return -1;
+
+ switch (type())
+ {
+ case node_document:
+ return 0;
+
+ case node_element:
+ case node_declaration:
+ case node_pi:
+ return (_root->header & impl::xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer;
+
+ case node_pcdata:
+ case node_cdata:
+ case node_comment:
+ case node_doctype:
+ return (_root->header & impl::xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer;
+
+ default:
+ return -1;
+ }
+ }
+
+#ifdef __BORLANDC__
+ PUGI__FN bool operator&&(const xml_node& lhs, bool rhs)
+ {
+ return (bool)lhs && rhs;
+ }
+
+ PUGI__FN bool operator||(const xml_node& lhs, bool rhs)
+ {
+ return (bool)lhs || rhs;
+ }
+#endif
+
+ PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root)
+ {
+ }
+
+ PUGI__FN xml_node_struct* xml_text::_data() const
+ {
+ if (!_root || impl::is_text_node(_root)) return _root;
+
+ for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)
+ if (impl::is_text_node(node))
+ return node;
+
+ return 0;
+ }
+
+ PUGI__FN xml_node_struct* xml_text::_data_new()
+ {
+ xml_node_struct* d = _data();
+ if (d) return d;
+
+ return xml_node(_root).append_child(node_pcdata).internal_object();
+ }
+
+ PUGI__FN xml_text::xml_text(): _root(0)
+ {
+ }
+
+ PUGI__FN static void unspecified_bool_xml_text(xml_text***)
+ {
+ }
+
+ PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const
+ {
+ return _data() ? unspecified_bool_xml_text : 0;
+ }
+
+ PUGI__FN bool xml_text::operator!() const
+ {
+ return !_data();
+ }
+
+ PUGI__FN bool xml_text::empty() const
+ {
+ return _data() == 0;
+ }
+
+ PUGI__FN const char_t* xml_text::get() const
+ {
+ xml_node_struct* d = _data();
+
+ return (d && d->value) ? d->value : PUGIXML_TEXT("");
+ }
+
+ PUGI__FN const char_t* xml_text::as_string(const char_t* def) const
+ {
+ xml_node_struct* d = _data();
+
+ return (d && d->value) ? d->value : def;
+ }
+
+ PUGI__FN int xml_text::as_int(int def) const
+ {
+ xml_node_struct* d = _data();
+
+ return impl::get_value_int(d ? d->value : 0, def);
+ }
+
+ PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
+ {
+ xml_node_struct* d = _data();
+
+ return impl::get_value_uint(d ? d->value : 0, def);
+ }
+
+ PUGI__FN double xml_text::as_double(double def) const
+ {
+ xml_node_struct* d = _data();
+
+ return impl::get_value_double(d ? d->value : 0, def);
+ }
+
+ PUGI__FN float xml_text::as_float(float def) const
+ {
+ xml_node_struct* d = _data();
+
+ return impl::get_value_float(d ? d->value : 0, def);
+ }
+
+ PUGI__FN bool xml_text::as_bool(bool def) const
+ {
+ xml_node_struct* d = _data();
+
+ return impl::get_value_bool(d ? d->value : 0, def);
+ }
+
+ PUGI__FN bool xml_text::set(const char_t* rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ }
+
+ PUGI__FN bool xml_text::set(int rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ }
+
+ PUGI__FN bool xml_text::set(unsigned int rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ }
+
+ PUGI__FN bool xml_text::set(double rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ }
+
+ PUGI__FN bool xml_text::set(bool rhs)
+ {
+ xml_node_struct* dn = _data_new();
+
+ return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+ }
+
+ PUGI__FN xml_text& xml_text::operator=(const char_t* rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_text& xml_text::operator=(int rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_text& xml_text::operator=(unsigned int rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_text& xml_text::operator=(double rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_text& xml_text::operator=(bool rhs)
+ {
+ set(rhs);
+ return *this;
+ }
+
+ PUGI__FN xml_node xml_text::data() const
+ {
+ return xml_node(_data());
+ }
+
+#ifdef __BORLANDC__
+ PUGI__FN bool operator&&(const xml_text& lhs, bool rhs)
+ {
+ return (bool)lhs && rhs;
+ }
+
+ PUGI__FN bool operator||(const xml_text& lhs, bool rhs)
+ {
+ return (bool)lhs || rhs;
+ }
+#endif
+
+ PUGI__FN xml_node_iterator::xml_node_iterator()
+ {
+ }
+
+ PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())
+ {
+ }
+
+ PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
+ {
+ }
+
+ PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const
+ {
+ return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
+ }
+
+ PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
+ {
+ return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
+ }
+
+ PUGI__FN xml_node& xml_node_iterator::operator*() const
+ {
+ assert(_wrap._root);
+ return _wrap;
+ }
+
+ PUGI__FN xml_node* xml_node_iterator::operator->() const
+ {
+ assert(_wrap._root);
+ return const_cast<xml_node*>(&_wrap); // BCC32 workaround
+ }
+
+ PUGI__FN const xml_node_iterator& xml_node_iterator::operator++()
+ {
+ assert(_wrap._root);
+ _wrap._root = _wrap._root->next_sibling;
+ return *this;
+ }
+
+ PUGI__FN xml_node_iterator xml_node_iterator::operator++(int)
+ {
+ xml_node_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ PUGI__FN const xml_node_iterator& xml_node_iterator::operator--()
+ {
+ _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child();
+ return *this;
+ }
+
+ PUGI__FN xml_node_iterator xml_node_iterator::operator--(int)
+ {
+ xml_node_iterator temp = *this;
+ --*this;
+ return temp;
+ }
+
+ PUGI__FN xml_attribute_iterator::xml_attribute_iterator()
+ {
+ }
+
+ PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)
+ {
+ }
+
+ PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
+ {
+ }
+
+ PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const
+ {
+ return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;
+ }
+
+ PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
+ {
+ return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;
+ }
+
+ PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const
+ {
+ assert(_wrap._attr);
+ return _wrap;
+ }
+
+ PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const
+ {
+ assert(_wrap._attr);
+ return const_cast<xml_attribute*>(&_wrap); // BCC32 workaround
+ }
+
+ PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++()
+ {
+ assert(_wrap._attr);
+ _wrap._attr = _wrap._attr->next_attribute;
+ return *this;
+ }
+
+ PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int)
+ {
+ xml_attribute_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--()
+ {
+ _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute();
+ return *this;
+ }
+
+ PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int)
+ {
+ xml_attribute_iterator temp = *this;
+ --*this;
+ return temp;
+ }
+
+ PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0)
+ {
+ }
+
+ PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _node(node), _name(name)
+ {
+ }
+
+ PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const
+ {
+ return _node == rhs._node;
+ }
+
+ PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const
+ {
+ return _node != rhs._node;
+ }
+
+ PUGI__FN xml_node& xml_named_node_iterator::operator*() const
+ {
+ assert(_node._root);
+ return _node;
+ }
+
+ PUGI__FN xml_node* xml_named_node_iterator::operator->() const
+ {
+ assert(_node._root);
+ return const_cast<xml_node*>(&_node); // BCC32 workaround
+ }
+
+ PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()
+ {
+ assert(_node._root);
+ _node = _node.next_sibling(_name);
+ return *this;
+ }
+
+ PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int)
+ {
+ xml_named_node_iterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto)
+ {
+ }
+
+ PUGI__FN xml_parse_result::operator bool() const
+ {
+ return status == status_ok;
+ }
+
+ PUGI__FN const char* xml_parse_result::description() const
+ {
+ switch (status)
+ {
+ case status_ok: return "No error";
+
+ case status_file_not_found: return "File was not found";
+ case status_io_error: return "Error reading from file/stream";
+ case status_out_of_memory: return "Could not allocate memory";
+ case status_internal_error: return "Internal error occurred";
+
+ case status_unrecognized_tag: return "Could not determine tag type";
+
+ case status_bad_pi: return "Error parsing document declaration/processing instruction";
+ case status_bad_comment: return "Error parsing comment";
+ case status_bad_cdata: return "Error parsing CDATA section";
+ case status_bad_doctype: return "Error parsing document type declaration";
+ case status_bad_pcdata: return "Error parsing PCDATA section";
+ case status_bad_start_element: return "Error parsing start element tag";
+ case status_bad_attribute: return "Error parsing element attribute";
+ case status_bad_end_element: return "Error parsing end element tag";
+ case status_end_element_mismatch: return "Start-end tags mismatch";
+
+ default: return "Unknown error";
+ }
+ }
+
+ PUGI__FN xml_document::xml_document(): _buffer(0)
+ {
+ create();
+ }
+
+ PUGI__FN xml_document::~xml_document()
+ {
+ destroy();
+ }
+
+ PUGI__FN void xml_document::reset()
+ {
+ destroy();
+ create();
+ }
+
+ PUGI__FN void xml_document::reset(const xml_document& proto)
+ {
+ reset();
+
+ for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling())
+ append_copy(cur);
+ }
+
+ PUGI__FN void xml_document::create()
+ {
+ // initialize sentinel page
+ PUGI__STATIC_ASSERT(offsetof(impl::xml_memory_page, data) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment <= sizeof(_memory));
+
+ // align upwards to page boundary
+ void* page_memory = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1));
+
+ // prepare page structure
+ impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory);
+
+ page->busy_size = impl::xml_memory_page_size;
+
+ // allocate new root
+ _root = new (page->data) impl::xml_document_struct(page);
+ _root->prev_sibling_c = _root;
+
+ // setup sentinel page
+ page->allocator = static_cast<impl::xml_document_struct*>(_root);
+ }
+
+ PUGI__FN void xml_document::destroy()
+ {
+ // destroy static storage
+ if (_buffer)
+ {
+ impl::xml_memory::deallocate(_buffer);
+ _buffer = 0;
+ }
+
+ // destroy dynamic storage, leave sentinel page (it's in static memory)
+ if (_root)
+ {
+ impl::xml_memory_page* root_page = reinterpret_cast<impl::xml_memory_page*>(_root->header & impl::xml_memory_page_pointer_mask);
+ assert(root_page && !root_page->prev && !root_page->memory);
+
+ // destroy all pages
+ for (impl::xml_memory_page* page = root_page->next; page; )
+ {
+ impl::xml_memory_page* next = page->next;
+
+ impl::xml_allocator::deallocate_page(page);
+
+ page = next;
+ }
+
+ // cleanup root page
+ root_page->allocator = 0;
+ root_page->next = 0;
+ root_page->busy_size = root_page->freed_size = 0;
+
+ _root = 0;
+ }
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
+ {
+ reset();
+
+ return impl::load_stream_impl(*this, stream, options, encoding);
+ }
+
+ PUGI__FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
+ {
+ reset();
+
+ return impl::load_stream_impl(*this, stream, options, encoding_wchar);
+ }
+#endif
+
+ PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
+ {
+ // Force native encoding (skip autodetection)
+ #ifdef PUGIXML_WCHAR_MODE
+ xml_encoding encoding = encoding_wchar;
+ #else
+ xml_encoding encoding = encoding_utf8;
+ #endif
+
+ return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding);
+ }
+
+ PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)
+ {
+ reset();
+
+ FILE* file = fopen(path_, "rb");
+
+ return impl::load_file_impl(*this, file, options, encoding);
+ }
+
+ PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
+ {
+ reset();
+
+ FILE* file = impl::open_file_wide(path_, L"rb");
+
+ return impl::load_file_impl(*this, file, options, encoding);
+ }
+
+ PUGI__FN xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own)
+ {
+ reset();
+
+ // check input buffer
+ assert(contents || size == 0);
+
+ // get actual encoding
+ xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
+
+ // get private buffer
+ char_t* buffer = 0;
+ size_t length = 0;
+
+ if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
+
+ // delete original buffer if we performed a conversion
+ if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
+
+ // parse
+ xml_parse_result res = impl::xml_parser::parse(buffer, length, _root, options);
+
+ // remember encoding
+ res.encoding = buffer_encoding;
+
+ // grab onto buffer if it's our buffer, user is responsible for deallocating contens himself
+ if (own || buffer != contents) _buffer = buffer;
+
+ return res;
+ }
+
+ PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
+ {
+ return load_buffer_impl(const_cast<void*>(contents), size, options, encoding, false, false);
+ }
+
+ PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)
+ {
+ return load_buffer_impl(contents, size, options, encoding, true, false);
+ }
+
+ PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)
+ {
+ return load_buffer_impl(contents, size, options, encoding, true, true);
+ }
+
+ PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+ {
+ impl::xml_buffered_writer buffered_writer(writer, encoding);
+
+ if ((flags & format_write_bom) && encoding != encoding_latin1)
+ {
+ // BOM always represents the codepoint U+FEFF, so just write it in native encoding
+ #ifdef PUGIXML_WCHAR_MODE
+ unsigned int bom = 0xfeff;
+ buffered_writer.write(static_cast<wchar_t>(bom));
+ #else
+ buffered_writer.write('\xef', '\xbb', '\xbf');
+ #endif
+ }
+
+ if (!(flags & format_no_declaration) && !impl::has_declaration(*this))
+ {
+ buffered_writer.write(PUGIXML_TEXT("<?xml version=\"1.0\""));
+ if (encoding == encoding_latin1) buffered_writer.write(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
+ buffered_writer.write('?', '>');
+ if (!(flags & format_raw)) buffered_writer.write('\n');
+ }
+
+ impl::node_output(buffered_writer, *this, indent, flags, 0);
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+ {
+ xml_writer_stream writer(stream);
+
+ save(writer, indent, flags, encoding);
+ }
+
+ PUGI__FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const
+ {
+ xml_writer_stream writer(stream);
+
+ save(writer, indent, flags, encoding_wchar);
+ }
+#endif
+
+ PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+ {
+ FILE* file = fopen(path_, (flags & format_save_file_text) ? "w" : "wb");
+ return impl::save_file_impl(*this, file, indent, flags, encoding);
+ }
+
+ PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+ {
+ FILE* file = impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb");
+ return impl::save_file_impl(*this, file, indent, flags, encoding);
+ }
+
+ PUGI__FN xml_node xml_document::document_element() const
+ {
+ for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+ if ((i->header & impl::xml_memory_page_type_mask) + 1 == node_element)
+ return xml_node(i);
+
+ return xml_node();
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)
+ {
+ assert(str);
+
+ return impl::as_utf8_impl(str, wcslen(str));
+ }
+
+ PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)
+ {
+ return impl::as_utf8_impl(str.c_str(), str.size());
+ }
+
+ PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)
+ {
+ assert(str);
+
+ return impl::as_wide_impl(str, strlen(str));
+ }
+
+ PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)
+ {
+ return impl::as_wide_impl(str.c_str(), str.size());
+ }
+#endif
+
+ PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)
+ {
+ impl::xml_memory::allocate = allocate;
+ impl::xml_memory::deallocate = deallocate;
+ }
+
+ PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()
+ {
+ return impl::xml_memory::allocate;
+ }
+
+ PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()
+ {
+ return impl::xml_memory::deallocate;
+ }
+}
+
+#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
+namespace std
+{
+ // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
+ PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)
+ {
+ return std::bidirectional_iterator_tag();
+ }
+
+ PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)
+ {
+ return std::bidirectional_iterator_tag();
+ }
+
+ PUGI__FN std::forward_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
+ {
+ return std::forward_iterator_tag();
+ }
+}
+#endif
+
+#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
+namespace std
+{
+ // Workarounds for (non-standard) iterator category detection
+ PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)
+ {
+ return std::bidirectional_iterator_tag();
+ }
+
+ PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)
+ {
+ return std::bidirectional_iterator_tag();
+ }
+
+ PUGI__FN std::forward_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
+ {
+ return std::forward_iterator_tag();
+ }
+}
+#endif
+
+#ifndef PUGIXML_NO_XPATH
+
+// STL replacements
+PUGI__NS_BEGIN
+ struct equal_to
+ {
+ template <typename T> bool operator()(const T& lhs, const T& rhs) const
+ {
+ return lhs == rhs;
+ }
+ };
+
+ struct not_equal_to
+ {
+ template <typename T> bool operator()(const T& lhs, const T& rhs) const
+ {
+ return lhs != rhs;
+ }
+ };
+
+ struct less
+ {
+ template <typename T> bool operator()(const T& lhs, const T& rhs) const
+ {
+ return lhs < rhs;
+ }
+ };
+
+ struct less_equal
+ {
+ template <typename T> bool operator()(const T& lhs, const T& rhs) const
+ {
+ return lhs <= rhs;
+ }
+ };
+
+ template <typename T> void swap(T& lhs, T& rhs)
+ {
+ T temp = lhs;
+ lhs = rhs;
+ rhs = temp;
+ }
+
+ template <typename I, typename Pred> I min_element(I begin, I end, const Pred& pred)
+ {
+ I result = begin;
+
+ for (I it = begin + 1; it != end; ++it)
+ if (pred(*it, *result))
+ result = it;
+
+ return result;
+ }
+
+ template <typename I> void reverse(I begin, I end)
+ {
+ while (begin + 1 < end) swap(*begin++, *--end);
+ }
+
+ template <typename I> I unique(I begin, I end)
+ {
+ // fast skip head
+ while (begin + 1 < end && *begin != *(begin + 1)) begin++;
+
+ if (begin == end) return begin;
+
+ // last written element
+ I write = begin++;
+
+ // merge unique elements
+ while (begin != end)
+ {
+ if (*begin != *write)
+ *++write = *begin++;
+ else
+ begin++;
+ }
+
+ // past-the-end (write points to live element)
+ return write + 1;
+ }
+
+ template <typename I> void copy_backwards(I begin, I end, I target)
+ {
+ while (begin != end) *--target = *--end;
+ }
+
+ template <typename I, typename Pred, typename T> void insertion_sort(I begin, I end, const Pred& pred, T*)
+ {
+ assert(begin != end);
+
+ for (I it = begin + 1; it != end; ++it)
+ {
+ T val = *it;
+
+ if (pred(val, *begin))
+ {
+ // move to front
+ copy_backwards(begin, it, it + 1);
+ *begin = val;
+ }
+ else
+ {
+ I hole = it;
+
+ // move hole backwards
+ while (pred(val, *(hole - 1)))
+ {
+ *hole = *(hole - 1);
+ hole--;
+ }
+
+ // fill hole with element
+ *hole = val;
+ }
+ }
+ }
+
+ // std variant for elements with ==
+ template <typename I, typename Pred> void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend)
+ {
+ I eqbeg = middle, eqend = middle + 1;
+
+ // expand equal range
+ while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg;
+ while (eqend != end && *eqend == *eqbeg) ++eqend;
+
+ // process outer elements
+ I ltend = eqbeg, gtbeg = eqend;
+
+ for (;;)
+ {
+ // find the element from the right side that belongs to the left one
+ for (; gtbeg != end; ++gtbeg)
+ if (!pred(*eqbeg, *gtbeg))
+ {
+ if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++);
+ else break;
+ }
+
+ // find the element from the left side that belongs to the right one
+ for (; ltend != begin; --ltend)
+ if (!pred(*(ltend - 1), *eqbeg))
+ {
+ if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg);
+ else break;
+ }
+
+ // scanned all elements
+ if (gtbeg == end && ltend == begin)
+ {
+ *out_eqbeg = eqbeg;
+ *out_eqend = eqend;
+ return;
+ }
+
+ // make room for elements by moving equal area
+ if (gtbeg == end)
+ {
+ if (--ltend != --eqbeg) swap(*ltend, *eqbeg);
+ swap(*eqbeg, *--eqend);
+ }
+ else if (ltend == begin)
+ {
+ if (eqend != gtbeg) swap(*eqbeg, *eqend);
+ ++eqend;
+ swap(*gtbeg++, *eqbeg++);
+ }
+ else swap(*gtbeg++, *--ltend);
+ }
+ }
+
+ template <typename I, typename Pred> void median3(I first, I middle, I last, const Pred& pred)
+ {
+ if (pred(*middle, *first)) swap(*middle, *first);
+ if (pred(*last, *middle)) swap(*last, *middle);
+ if (pred(*middle, *first)) swap(*middle, *first);
+ }
+
+ template <typename I, typename Pred> void median(I first, I middle, I last, const Pred& pred)
+ {
+ if (last - first <= 40)
+ {
+ // median of three for small chunks
+ median3(first, middle, last, pred);
+ }
+ else
+ {
+ // median of nine
+ size_t step = (last - first + 1) / 8;
+
+ median3(first, first + step, first + 2 * step, pred);
+ median3(middle - step, middle, middle + step, pred);
+ median3(last - 2 * step, last - step, last, pred);
+ median3(first + step, middle, last - step, pred);
+ }
+ }
+
+ template <typename I, typename Pred> void sort(I begin, I end, const Pred& pred)
+ {
+ // sort large chunks
+ while (end - begin > 32)
+ {
+ // find median element
+ I middle = begin + (end - begin) / 2;
+ median(begin, middle, end - 1, pred);
+
+ // partition in three chunks (< = >)
+ I eqbeg, eqend;
+ partition(begin, middle, end, pred, &eqbeg, &eqend);
+
+ // loop on larger half
+ if (eqbeg - begin > end - eqend)
+ {
+ sort(eqend, end, pred);
+ end = eqbeg;
+ }
+ else
+ {
+ sort(begin, eqbeg, pred);
+ begin = eqend;
+ }
+ }
+
+ // insertion sort small chunk
+ if (begin != end) insertion_sort(begin, end, pred, &*begin);
+ }
+PUGI__NS_END
+
+// Allocator used for AST and evaluation stacks
+PUGI__NS_BEGIN
+ struct xpath_memory_block
+ {
+ xpath_memory_block* next;
+
+ char data[
+ #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
+ PUGIXML_MEMORY_XPATH_PAGE_SIZE
+ #else
+ 4096
+ #endif
+ ];
+ };
+
+ class xpath_allocator
+ {
+ xpath_memory_block* _root;
+ size_t _root_size;
+
+ public:
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ jmp_buf* error_handler;
+ #endif
+
+ xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ error_handler = 0;
+ #endif
+ }
+
+ void* allocate_nothrow(size_t size)
+ {
+ const size_t block_capacity = sizeof(_root->data);
+
+ // align size so that we're able to store pointers in subsequent blocks
+ size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
+
+ if (_root_size + size <= block_capacity)
+ {
+ void* buf = _root->data + _root_size;
+ _root_size += size;
+ return buf;
+ }
+ else
+ {
+ size_t block_data_size = (size > block_capacity) ? size : block_capacity;
+ size_t block_size = block_data_size + offsetof(xpath_memory_block, data);
+
+ xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));
+ if (!block) return 0;
+
+ block->next = _root;
+
+ _root = block;
+ _root_size = size;
+
+ return block->data;
+ }
+ }
+
+ void* allocate(size_t size)
+ {
+ void* result = allocate_nothrow(size);
+
+ if (!result)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ assert(error_handler);
+ longjmp(*error_handler, 1);
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
+ return result;
+ }
+
+ void* reallocate(void* ptr, size_t old_size, size_t new_size)
+ {
+ // align size so that we're able to store pointers in subsequent blocks
+ old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
+ new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
+
+ // we can only reallocate the last object
+ assert(ptr == 0 || static_cast<char*>(ptr) + old_size == _root->data + _root_size);
+
+ // adjust root size so that we have not allocated the object at all
+ bool only_object = (_root_size == old_size);
+
+ if (ptr) _root_size -= old_size;
+
+ // allocate a new version (this will obviously reuse the memory if possible)
+ void* result = allocate(new_size);
+ assert(result);
+
+ // we have a new block
+ if (result != ptr && ptr)
+ {
+ // copy old data
+ assert(new_size > old_size);
+ memcpy(result, ptr, old_size);
+
+ // free the previous page if it had no other objects
+ if (only_object)
+ {
+ assert(_root->data == result);
+ assert(_root->next);
+
+ xpath_memory_block* next = _root->next->next;
+
+ if (next)
+ {
+ // deallocate the whole page, unless it was the first one
+ xml_memory::deallocate(_root->next);
+ _root->next = next;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ void revert(const xpath_allocator& state)
+ {
+ // free all new pages
+ xpath_memory_block* cur = _root;
+
+ while (cur != state._root)
+ {
+ xpath_memory_block* next = cur->next;
+
+ xml_memory::deallocate(cur);
+
+ cur = next;
+ }
+
+ // restore state
+ _root = state._root;
+ _root_size = state._root_size;
+ }
+
+ void release()
+ {
+ xpath_memory_block* cur = _root;
+ assert(cur);
+
+ while (cur->next)
+ {
+ xpath_memory_block* next = cur->next;
+
+ xml_memory::deallocate(cur);
+
+ cur = next;
+ }
+ }
+ };
+
+ struct xpath_allocator_capture
+ {
+ xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc)
+ {
+ }
+
+ ~xpath_allocator_capture()
+ {
+ _target->revert(_state);
+ }
+
+ xpath_allocator* _target;
+ xpath_allocator _state;
+ };
+
+ struct xpath_stack
+ {
+ xpath_allocator* result;
+ xpath_allocator* temp;
+ };
+
+ struct xpath_stack_data
+ {
+ xpath_memory_block blocks[2];
+ xpath_allocator result;
+ xpath_allocator temp;
+ xpath_stack stack;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ jmp_buf error_handler;
+ #endif
+
+ xpath_stack_data(): result(blocks + 0), temp(blocks + 1)
+ {
+ blocks[0].next = blocks[1].next = 0;
+
+ stack.result = &result;
+ stack.temp = &temp;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ result.error_handler = temp.error_handler = &error_handler;
+ #endif
+ }
+
+ ~xpath_stack_data()
+ {
+ result.release();
+ temp.release();
+ }
+ };
+PUGI__NS_END
+
+// String class
+PUGI__NS_BEGIN
+ class xpath_string
+ {
+ const char_t* _buffer;
+ bool _uses_heap;
+
+ static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
+ {
+ char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));
+ assert(result);
+
+ memcpy(result, string, length * sizeof(char_t));
+ result[length] = 0;
+
+ return result;
+ }
+
+ static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc)
+ {
+ return duplicate_string(string, strlength(string), alloc);
+ }
+
+ public:
+ xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false)
+ {
+ }
+
+ explicit xpath_string(const char_t* str, xpath_allocator* alloc)
+ {
+ bool empty_ = (*str == 0);
+
+ _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(str, alloc);
+ _uses_heap = !empty_;
+ }
+
+ explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap)
+ {
+ }
+
+ xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc)
+ {
+ assert(begin <= end);
+
+ bool empty_ = (begin == end);
+
+ _buffer = empty_ ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast<size_t>(end - begin), alloc);
+ _uses_heap = !empty_;
+ }
+
+ void append(const xpath_string& o, xpath_allocator* alloc)
+ {
+ // skip empty sources
+ if (!*o._buffer) return;
+
+ // fast append for constant empty target and constant source
+ if (!*_buffer && !_uses_heap && !o._uses_heap)
+ {
+ _buffer = o._buffer;
+ }
+ else
+ {
+ // need to make heap copy
+ size_t target_length = strlength(_buffer);
+ size_t source_length = strlength(o._buffer);
+ size_t result_length = target_length + source_length;
+
+ // allocate new buffer
+ char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t)));
+ assert(result);
+
+ // append first string to the new buffer in case there was no reallocation
+ if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t));
+
+ // append second string to the new buffer
+ memcpy(result + target_length, o._buffer, source_length * sizeof(char_t));
+ result[result_length] = 0;
+
+ // finalize
+ _buffer = result;
+ _uses_heap = true;
+ }
+ }
+
+ const char_t* c_str() const
+ {
+ return _buffer;
+ }
+
+ size_t length() const
+ {
+ return strlength(_buffer);
+ }
+
+ char_t* data(xpath_allocator* alloc)
+ {
+ // make private heap copy
+ if (!_uses_heap)
+ {
+ _buffer = duplicate_string(_buffer, alloc);
+ _uses_heap = true;
+ }
+
+ return const_cast<char_t*>(_buffer);
+ }
+
+ bool empty() const
+ {
+ return *_buffer == 0;
+ }
+
+ bool operator==(const xpath_string& o) const
+ {
+ return strequal(_buffer, o._buffer);
+ }
+
+ bool operator!=(const xpath_string& o) const
+ {
+ return !strequal(_buffer, o._buffer);
+ }
+
+ bool uses_heap() const
+ {
+ return _uses_heap;
+ }
+ };
+
+ PUGI__FN xpath_string xpath_string_const(const char_t* str)
+ {
+ return xpath_string(str, false);
+ }
+PUGI__NS_END
+
+PUGI__NS_BEGIN
+ PUGI__FN bool starts_with(const char_t* string, const char_t* pattern)
+ {
+ while (*pattern && *string == *pattern)
+ {
+ string++;
+ pattern++;
+ }
+
+ return *pattern == 0;
+ }
+
+ PUGI__FN const char_t* find_char(const char_t* s, char_t c)
+ {
+ #ifdef PUGIXML_WCHAR_MODE
+ return wcschr(s, c);
+ #else
+ return strchr(s, c);
+ #endif
+ }
+
+ PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p)
+ {
+ #ifdef PUGIXML_WCHAR_MODE
+ // MSVC6 wcsstr bug workaround (if s is empty it always returns 0)
+ return (*p == 0) ? s : wcsstr(s, p);
+ #else
+ return strstr(s, p);
+ #endif
+ }
+
+ // Converts symbol to lower case, if it is an ASCII one
+ PUGI__FN char_t tolower_ascii(char_t ch)
+ {
+ return static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;
+ }
+
+ PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)
+ {
+ if (na.attribute())
+ return xpath_string_const(na.attribute().value());
+ else
+ {
+ const xml_node& n = na.node();
+
+ switch (n.type())
+ {
+ case node_pcdata:
+ case node_cdata:
+ case node_comment:
+ case node_pi:
+ return xpath_string_const(n.value());
+
+ case node_document:
+ case node_element:
+ {
+ xpath_string result;
+
+ xml_node cur = n.first_child();
+
+ while (cur && cur != n)
+ {
+ if (cur.type() == node_pcdata || cur.type() == node_cdata)
+ result.append(xpath_string_const(cur.value()), alloc);
+
+ if (cur.first_child())
+ cur = cur.first_child();
+ else if (cur.next_sibling())
+ cur = cur.next_sibling();
+ else
+ {
+ while (!cur.next_sibling() && cur != n)
+ cur = cur.parent();
+
+ if (cur != n) cur = cur.next_sibling();
+ }
+ }
+
+ return result;
+ }
+
+ default:
+ return xpath_string();
+ }
+ }
+ }
+
+ PUGI__FN unsigned int node_height(xml_node n)
+ {
+ unsigned int result = 0;
+
+ while (n)
+ {
+ ++result;
+ n = n.parent();
+ }
+
+ return result;
+ }
+
+ PUGI__FN bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh)
+ {
+ // normalize heights
+ for (unsigned int i = rh; i < lh; i++) ln = ln.parent();
+ for (unsigned int j = lh; j < rh; j++) rn = rn.parent();
+
+ // one node is the ancestor of the other
+ if (ln == rn) return lh < rh;
+
+ // find common ancestor
+ while (ln.parent() != rn.parent())
+ {
+ ln = ln.parent();
+ rn = rn.parent();
+ }
+
+ // there is no common ancestor (the shared parent is null), nodes are from different documents
+ if (!ln.parent()) return ln < rn;
+
+ // determine sibling order
+ for (; ln; ln = ln.next_sibling())
+ if (ln == rn)
+ return true;
+
+ return false;
+ }
+
+ PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node)
+ {
+ while (node && node != parent) node = node.parent();
+
+ return parent && node == parent;
+ }
+
+ PUGI__FN const void* document_order(const xpath_node& xnode)
+ {
+ xml_node_struct* node = xnode.node().internal_object();
+
+ if (node)
+ {
+ if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name;
+ if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value;
+ return 0;
+ }
+
+ xml_attribute_struct* attr = xnode.attribute().internal_object();
+
+ if (attr)
+ {
+ if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name;
+ if ((attr->header & xml_memory_page_value_allocated_mask) == 0) return attr->value;
+ return 0;
+ }
+
+ return 0;
+ }
+
+ struct document_order_comparator
+ {
+ bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
+ {
+ // optimized document order based check
+ const void* lo = document_order(lhs);
+ const void* ro = document_order(rhs);
+
+ if (lo && ro) return lo < ro;
+
+ // slow comparison
+ xml_node ln = lhs.node(), rn = rhs.node();
+
+ // compare attributes
+ if (lhs.attribute() && rhs.attribute())
+ {
+ // shared parent
+ if (lhs.parent() == rhs.parent())
+ {
+ // determine sibling order
+ for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute())
+ if (a == rhs.attribute())
+ return true;
+
+ return false;
+ }
+
+ // compare attribute parents
+ ln = lhs.parent();
+ rn = rhs.parent();
+ }
+ else if (lhs.attribute())
+ {
+ // attributes go after the parent element
+ if (lhs.parent() == rhs.node()) return false;
+
+ ln = lhs.parent();
+ }
+ else if (rhs.attribute())
+ {
+ // attributes go after the parent element
+ if (rhs.parent() == lhs.node()) return true;
+
+ rn = rhs.parent();
+ }
+
+ if (ln == rn) return false;
+
+ unsigned int lh = node_height(ln);
+ unsigned int rh = node_height(rn);
+
+ return node_is_before(ln, lh, rn, rh);
+ }
+ };
+
+ struct duplicate_comparator
+ {
+ bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
+ {
+ if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;
+ else return rhs.attribute() ? false : lhs.node() < rhs.node();
+ }
+ };
+
+ PUGI__FN double gen_nan()
+ {
+ #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
+ union { float f; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1];
+ u[0].i = 0x7fc00000;
+ return u[0].f;
+ #else
+ // fallback
+ const volatile double zero = 0.0;
+ return zero / zero;
+ #endif
+ }
+
+ PUGI__FN bool is_nan(double value)
+ {
+ #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
+ return !!_isnan(value);
+ #elif defined(fpclassify) && defined(FP_NAN)
+ return fpclassify(value) == FP_NAN;
+ #else
+ // fallback
+ const volatile double v = value;
+ return v != v;
+ #endif
+ }
+
+ PUGI__FN const char_t* convert_number_to_string_special(double value)
+ {
+ #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
+ if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0;
+ if (_isnan(value)) return PUGIXML_TEXT("NaN");
+ return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
+ #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO)
+ switch (fpclassify(value))
+ {
+ case FP_NAN:
+ return PUGIXML_TEXT("NaN");
+
+ case FP_INFINITE:
+ return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
+
+ case FP_ZERO:
+ return PUGIXML_TEXT("0");
+
+ default:
+ return 0;
+ }
+ #else
+ // fallback
+ const volatile double v = value;
+
+ if (v == 0) return PUGIXML_TEXT("0");
+ if (v != v) return PUGIXML_TEXT("NaN");
+ if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
+ return 0;
+ #endif
+ }
+
+ PUGI__FN bool convert_number_to_boolean(double value)
+ {
+ return (value != 0 && !is_nan(value));
+ }
+
+ PUGI__FN void truncate_zeros(char* begin, char* end)
+ {
+ while (begin != end && end[-1] == '0') end--;
+
+ *end = 0;
+ }
+
+ // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
+#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)
+ {
+ // get base values
+ int sign, exponent;
+ _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign);
+
+ // truncate redundant zeros
+ truncate_zeros(buffer, buffer + strlen(buffer));
+
+ // fill results
+ *out_mantissa = buffer;
+ *out_exponent = exponent;
+ }
+#else
+ PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent)
+ {
+ // get a scientific notation value with IEEE DBL_DIG decimals
+ sprintf(buffer, "%.*e", DBL_DIG, value);
+ assert(strlen(buffer) < buffer_size);
+ (void)!buffer_size;
+
+ // get the exponent (possibly negative)
+ char* exponent_string = strchr(buffer, 'e');
+ assert(exponent_string);
+
+ int exponent = atoi(exponent_string + 1);
+
+ // extract mantissa string: skip sign
+ char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer;
+ assert(mantissa[0] != '0' && mantissa[1] == '.');
+
+ // divide mantissa by 10 to eliminate integer part
+ mantissa[1] = mantissa[0];
+ mantissa++;
+ exponent++;
+
+ // remove extra mantissa digits and zero-terminate mantissa
+ truncate_zeros(mantissa, exponent_string);
+
+ // fill results
+ *out_mantissa = mantissa;
+ *out_exponent = exponent;
+ }
+#endif
+
+ PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc)
+ {
+ // try special number conversion
+ const char_t* special = convert_number_to_string_special(value);
+ if (special) return xpath_string_const(special);
+
+ // get mantissa + exponent form
+ char mantissa_buffer[64];
+
+ char* mantissa;
+ int exponent;
+ convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent);
+
+ // make the number!
+ char_t result[512];
+ char_t* s = result;
+
+ // sign
+ if (value < 0) *s++ = '-';
+
+ // integer part
+ if (exponent <= 0)
+ {
+ *s++ = '0';
+ }
+ else
+ {
+ while (exponent > 0)
+ {
+ assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);
+ *s++ = *mantissa ? *mantissa++ : '0';
+ exponent--;
+ }
+ }
+
+ // fractional part
+ if (*mantissa)
+ {
+ // decimal point
+ *s++ = '.';
+
+ // extra zeroes from negative exponent
+ while (exponent < 0)
+ {
+ *s++ = '0';
+ exponent++;
+ }
+
+ // extra mantissa digits
+ while (*mantissa)
+ {
+ assert(static_cast<unsigned int>(*mantissa - '0') <= 9);
+ *s++ = *mantissa++;
+ }
+ }
+
+ // zero-terminate
+ assert(s < result + sizeof(result) / sizeof(result[0]));
+ *s = 0;
+
+ return xpath_string(result, alloc);
+ }
+
+ PUGI__FN bool check_string_to_number_format(const char_t* string)
+ {
+ // parse leading whitespace
+ while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
+
+ // parse sign
+ if (*string == '-') ++string;
+
+ if (!*string) return false;
+
+ // if there is no integer part, there should be a decimal part with at least one digit
+ if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false;
+
+ // parse integer part
+ while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
+
+ // parse decimal part
+ if (*string == '.')
+ {
+ ++string;
+
+ while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
+ }
+
+ // parse trailing whitespace
+ while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
+
+ return *string == 0;
+ }
+
+ PUGI__FN double convert_string_to_number(const char_t* string)
+ {
+ // check string format
+ if (!check_string_to_number_format(string)) return gen_nan();
+
+ // parse string
+ #ifdef PUGIXML_WCHAR_MODE
+ return wcstod(string, 0);
+ #else
+ return atof(string);
+ #endif
+ }
+
+ PUGI__FN bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result)
+ {
+ char_t buffer[32];
+
+ size_t length = static_cast<size_t>(end - begin);
+ char_t* scratch = buffer;
+
+ if (length >= sizeof(buffer) / sizeof(buffer[0]))
+ {
+ // need to make dummy on-heap copy
+ scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+ if (!scratch) return false;
+ }
+
+ // copy string to zero-terminated buffer and perform conversion
+ memcpy(scratch, begin, length * sizeof(char_t));
+ scratch[length] = 0;
+
+ *out_result = convert_string_to_number(scratch);
+
+ // free dummy buffer
+ if (scratch != buffer) xml_memory::deallocate(scratch);
+
+ return true;
+ }
+
+ PUGI__FN double round_nearest(double value)
+ {
+ return floor(value + 0.5);
+ }
+
+ PUGI__FN double round_nearest_nzero(double value)
+ {
+ // same as round_nearest, but returns -0 for [-0.5, -0]
+ // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)
+ return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);
+ }
+
+ PUGI__FN const char_t* qualified_name(const xpath_node& node)
+ {
+ return node.attribute() ? node.attribute().name() : node.node().name();
+ }
+
+ PUGI__FN const char_t* local_name(const xpath_node& node)
+ {
+ const char_t* name = qualified_name(node);
+ const char_t* p = find_char(name, ':');
+
+ return p ? p + 1 : name;
+ }
+
+ struct namespace_uri_predicate
+ {
+ const char_t* prefix;
+ size_t prefix_length;
+
+ namespace_uri_predicate(const char_t* name)
+ {
+ const char_t* pos = find_char(name, ':');
+
+ prefix = pos ? name : 0;
+ prefix_length = pos ? static_cast<size_t>(pos - name) : 0;
+ }
+
+ bool operator()(const xml_attribute& a) const
+ {
+ const char_t* name = a.name();
+
+ if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false;
+
+ return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0;
+ }
+ };
+
+ PUGI__FN const char_t* namespace_uri(const xml_node& node)
+ {
+ namespace_uri_predicate pred = node.name();
+
+ xml_node p = node;
+
+ while (p)
+ {
+ xml_attribute a = p.find_attribute(pred);
+
+ if (a) return a.value();
+
+ p = p.parent();
+ }
+
+ return PUGIXML_TEXT("");
+ }
+
+ PUGI__FN const char_t* namespace_uri(const xml_attribute& attr, const xml_node& parent)
+ {
+ namespace_uri_predicate pred = attr.name();
+
+ // Default namespace does not apply to attributes
+ if (!pred.prefix) return PUGIXML_TEXT("");
+
+ xml_node p = parent;
+
+ while (p)
+ {
+ xml_attribute a = p.find_attribute(pred);
+
+ if (a) return a.value();
+
+ p = p.parent();
+ }
+
+ return PUGIXML_TEXT("");
+ }
+
+ PUGI__FN const char_t* namespace_uri(const xpath_node& node)
+ {
+ return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());
+ }
+
+ PUGI__FN void normalize_space(char_t* buffer)
+ {
+ char_t* write = buffer;
+
+ for (char_t* it = buffer; *it; )
+ {
+ char_t ch = *it++;
+
+ if (PUGI__IS_CHARTYPE(ch, ct_space))
+ {
+ // replace whitespace sequence with single space
+ while (PUGI__IS_CHARTYPE(*it, ct_space)) it++;
+
+ // avoid leading spaces
+ if (write != buffer) *write++ = ' ';
+ }
+ else *write++ = ch;
+ }
+
+ // remove trailing space
+ if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--;
+
+ // zero-terminate
+ *write = 0;
+ }
+
+ PUGI__FN void translate(char_t* buffer, const char_t* from, const char_t* to)
+ {
+ size_t to_length = strlength(to);
+
+ char_t* write = buffer;
+
+ while (*buffer)
+ {
+ PUGI__DMC_VOLATILE char_t ch = *buffer++;
+
+ const char_t* pos = find_char(from, ch);
+
+ if (!pos)
+ *write++ = ch; // do not process
+ else if (static_cast<size_t>(pos - from) < to_length)
+ *write++ = to[pos - from]; // replace
+ }
+
+ // zero-terminate
+ *write = 0;
+ }
+
+ struct xpath_variable_boolean: xpath_variable
+ {
+ xpath_variable_boolean(): value(false)
+ {
+ }
+
+ bool value;
+ char_t name[1];
+ };
+
+ struct xpath_variable_number: xpath_variable
+ {
+ xpath_variable_number(): value(0)
+ {
+ }
+
+ double value;
+ char_t name[1];
+ };
+
+ struct xpath_variable_string: xpath_variable
+ {
+ xpath_variable_string(): value(0)
+ {
+ }
+
+ ~xpath_variable_string()
+ {
+ if (value) xml_memory::deallocate(value);
+ }
+
+ char_t* value;
+ char_t name[1];
+ };
+
+ struct xpath_variable_node_set: xpath_variable
+ {
+ xpath_node_set value;
+ char_t name[1];
+ };
+
+ static const xpath_node_set dummy_node_set;
+
+ PUGI__FN unsigned int hash_string(const char_t* str)
+ {
+ // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
+ unsigned int result = 0;
+
+ while (*str)
+ {
+ result += static_cast<unsigned int>(*str++);
+ result += result << 10;
+ result ^= result >> 6;
+ }
+
+ result += result << 3;
+ result ^= result >> 11;
+ result += result << 15;
+
+ return result;
+ }
+
+ template <typename T> PUGI__FN T* new_xpath_variable(const char_t* name)
+ {
+ size_t length = strlength(name);
+ if (length == 0) return 0; // empty variable names are invalid
+
+ // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters
+ void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t));
+ if (!memory) return 0;
+
+ T* result = new (memory) T();
+
+ memcpy(result->name, name, (length + 1) * sizeof(char_t));
+
+ return result;
+ }
+
+ PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)
+ {
+ switch (type)
+ {
+ case xpath_type_node_set:
+ return new_xpath_variable<xpath_variable_node_set>(name);
+
+ case xpath_type_number:
+ return new_xpath_variable<xpath_variable_number>(name);
+
+ case xpath_type_string:
+ return new_xpath_variable<xpath_variable_string>(name);
+
+ case xpath_type_boolean:
+ return new_xpath_variable<xpath_variable_boolean>(name);
+
+ default:
+ return 0;
+ }
+ }
+
+ template <typename T> PUGI__FN void delete_xpath_variable(T* var)
+ {
+ var->~T();
+ xml_memory::deallocate(var);
+ }
+
+ PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)
+ {
+ switch (type)
+ {
+ case xpath_type_node_set:
+ delete_xpath_variable(static_cast<xpath_variable_node_set*>(var));
+ break;
+
+ case xpath_type_number:
+ delete_xpath_variable(static_cast<xpath_variable_number*>(var));
+ break;
+
+ case xpath_type_string:
+ delete_xpath_variable(static_cast<xpath_variable_string*>(var));
+ break;
+
+ case xpath_type_boolean:
+ delete_xpath_variable(static_cast<xpath_variable_boolean*>(var));
+ break;
+
+ default:
+ assert(!"Invalid variable type");
+ }
+ }
+
+ PUGI__FN xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end)
+ {
+ char_t buffer[32];
+
+ size_t length = static_cast<size_t>(end - begin);
+ char_t* scratch = buffer;
+
+ if (length >= sizeof(buffer) / sizeof(buffer[0]))
+ {
+ // need to make dummy on-heap copy
+ scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+ if (!scratch) return 0;
+ }
+
+ // copy string to zero-terminated buffer and perform lookup
+ memcpy(scratch, begin, length * sizeof(char_t));
+ scratch[length] = 0;
+
+ xpath_variable* result = set->get(scratch);
+
+ // free dummy buffer
+ if (scratch != buffer) xml_memory::deallocate(scratch);
+
+ return result;
+ }
+PUGI__NS_END
+
+// Internal node set class
+PUGI__NS_BEGIN
+ PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)
+ {
+ xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
+
+ if (type == xpath_node_set::type_unsorted)
+ {
+ sort(begin, end, document_order_comparator());
+
+ type = xpath_node_set::type_sorted;
+ }
+
+ if (type != order) reverse(begin, end);
+
+ return order;
+ }
+
+ PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)
+ {
+ if (begin == end) return xpath_node();
+
+ switch (type)
+ {
+ case xpath_node_set::type_sorted:
+ return *begin;
+
+ case xpath_node_set::type_sorted_reverse:
+ return *(end - 1);
+
+ case xpath_node_set::type_unsorted:
+ return *min_element(begin, end, document_order_comparator());
+
+ default:
+ assert(!"Invalid node set type");
+ return xpath_node();
+ }
+ }
+
+ class xpath_node_set_raw
+ {
+ xpath_node_set::type_t _type;
+
+ xpath_node* _begin;
+ xpath_node* _end;
+ xpath_node* _eos;
+
+ public:
+ xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0)
+ {
+ }
+
+ xpath_node* begin() const
+ {
+ return _begin;
+ }
+
+ xpath_node* end() const
+ {
+ return _end;
+ }
+
+ bool empty() const
+ {
+ return _begin == _end;
+ }
+
+ size_t size() const
+ {
+ return static_cast<size_t>(_end - _begin);
+ }
+
+ xpath_node first() const
+ {
+ return xpath_first(_begin, _end, _type);
+ }
+
+ void push_back(const xpath_node& node, xpath_allocator* alloc)
+ {
+ if (_end == _eos)
+ {
+ size_t capacity = static_cast<size_t>(_eos - _begin);
+
+ // get new capacity (1.5x rule)
+ size_t new_capacity = capacity + capacity / 2 + 1;
+
+ // reallocate the old array or allocate a new one
+ xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
+ assert(data);
+
+ // finalize
+ _begin = data;
+ _end = data + capacity;
+ _eos = data + new_capacity;
+ }
+
+ *_end++ = node;
+ }
+
+ void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)
+ {
+ size_t size_ = static_cast<size_t>(_end - _begin);
+ size_t capacity = static_cast<size_t>(_eos - _begin);
+ size_t count = static_cast<size_t>(end_ - begin_);
+
+ if (size_ + count > capacity)
+ {
+ // reallocate the old array or allocate a new one
+ xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node)));
+ assert(data);
+
+ // finalize
+ _begin = data;
+ _end = data + size_;
+ _eos = data + size_ + count;
+ }
+
+ memcpy(_end, begin_, count * sizeof(xpath_node));
+ _end += count;
+ }
+
+ void sort_do()
+ {
+ _type = xpath_sort(_begin, _end, _type, false);
+ }
+
+ void truncate(xpath_node* pos)
+ {
+ assert(_begin <= pos && pos <= _end);
+
+ _end = pos;
+ }
+
+ void remove_duplicates()
+ {
+ if (_type == xpath_node_set::type_unsorted)
+ sort(_begin, _end, duplicate_comparator());
+
+ _end = unique(_begin, _end);
+ }
+
+ xpath_node_set::type_t type() const
+ {
+ return _type;
+ }
+
+ void set_type(xpath_node_set::type_t value)
+ {
+ _type = value;
+ }
+ };
+PUGI__NS_END
+
+PUGI__NS_BEGIN
+ struct xpath_context
+ {
+ xpath_node n;
+ size_t position, size;
+
+ xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_)
+ {
+ }
+ };
+
+ enum lexeme_t
+ {
+ lex_none = 0,
+ lex_equal,
+ lex_not_equal,
+ lex_less,
+ lex_greater,
+ lex_less_or_equal,
+ lex_greater_or_equal,
+ lex_plus,
+ lex_minus,
+ lex_multiply,
+ lex_union,
+ lex_var_ref,
+ lex_open_brace,
+ lex_close_brace,
+ lex_quoted_string,
+ lex_number,
+ lex_slash,
+ lex_double_slash,
+ lex_open_square_brace,
+ lex_close_square_brace,
+ lex_string,
+ lex_comma,
+ lex_axis_attribute,
+ lex_dot,
+ lex_double_dot,
+ lex_double_colon,
+ lex_eof
+ };
+
+ struct xpath_lexer_string
+ {
+ const char_t* begin;
+ const char_t* end;
+
+ xpath_lexer_string(): begin(0), end(0)
+ {
+ }
+
+ bool operator==(const char_t* other) const
+ {
+ size_t length = static_cast<size_t>(end - begin);
+
+ return strequalrange(other, begin, length);
+ }
+ };
+
+ class xpath_lexer
+ {
+ const char_t* _cur;
+ const char_t* _cur_lexeme_pos;
+ xpath_lexer_string _cur_lexeme_contents;
+
+ lexeme_t _cur_lexeme;
+
+ public:
+ explicit xpath_lexer(const char_t* query): _cur(query)
+ {
+ next();
+ }
+
+ const char_t* state() const
+ {
+ return _cur;
+ }
+
+ void next()
+ {
+ const char_t* cur = _cur;
+
+ while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur;
+
+ // save lexeme position for error reporting
+ _cur_lexeme_pos = cur;
+
+ switch (*cur)
+ {
+ case 0:
+ _cur_lexeme = lex_eof;
+ break;
+
+ case '>':
+ if (*(cur+1) == '=')
+ {
+ cur += 2;
+ _cur_lexeme = lex_greater_or_equal;
+ }
+ else
+ {
+ cur += 1;
+ _cur_lexeme = lex_greater;
+ }
+ break;
+
+ case '<':
+ if (*(cur+1) == '=')
+ {
+ cur += 2;
+ _cur_lexeme = lex_less_or_equal;
+ }
+ else
+ {
+ cur += 1;
+ _cur_lexeme = lex_less;
+ }
+ break;
+
+ case '!':
+ if (*(cur+1) == '=')
+ {
+ cur += 2;
+ _cur_lexeme = lex_not_equal;
+ }
+ else
+ {
+ _cur_lexeme = lex_none;
+ }
+ break;
+
+ case '=':
+ cur += 1;
+ _cur_lexeme = lex_equal;
+
+ break;
+
+ case '+':
+ cur += 1;
+ _cur_lexeme = lex_plus;
+
+ break;
+
+ case '-':
+ cur += 1;
+ _cur_lexeme = lex_minus;
+
+ break;
+
+ case '*':
+ cur += 1;
+ _cur_lexeme = lex_multiply;
+
+ break;
+
+ case '|':
+ cur += 1;
+ _cur_lexeme = lex_union;
+
+ break;
+
+ case '$':
+ cur += 1;
+
+ if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
+ {
+ _cur_lexeme_contents.begin = cur;
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+
+ if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname
+ {
+ cur++; // :
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+ }
+
+ _cur_lexeme_contents.end = cur;
+
+ _cur_lexeme = lex_var_ref;
+ }
+ else
+ {
+ _cur_lexeme = lex_none;
+ }
+
+ break;
+
+ case '(':
+ cur += 1;
+ _cur_lexeme = lex_open_brace;
+
+ break;
+
+ case ')':
+ cur += 1;
+ _cur_lexeme = lex_close_brace;
+
+ break;
+
+ case '[':
+ cur += 1;
+ _cur_lexeme = lex_open_square_brace;
+
+ break;
+
+ case ']':
+ cur += 1;
+ _cur_lexeme = lex_close_square_brace;
+
+ break;
+
+ case ',':
+ cur += 1;
+ _cur_lexeme = lex_comma;
+
+ break;
+
+ case '/':
+ if (*(cur+1) == '/')
+ {
+ cur += 2;
+ _cur_lexeme = lex_double_slash;
+ }
+ else
+ {
+ cur += 1;
+ _cur_lexeme = lex_slash;
+ }
+ break;
+
+ case '.':
+ if (*(cur+1) == '.')
+ {
+ cur += 2;
+ _cur_lexeme = lex_double_dot;
+ }
+ else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit))
+ {
+ _cur_lexeme_contents.begin = cur; // .
+
+ ++cur;
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+
+ _cur_lexeme_contents.end = cur;
+
+ _cur_lexeme = lex_number;
+ }
+ else
+ {
+ cur += 1;
+ _cur_lexeme = lex_dot;
+ }
+ break;
+
+ case '@':
+ cur += 1;
+ _cur_lexeme = lex_axis_attribute;
+
+ break;
+
+ case '"':
+ case '\'':
+ {
+ char_t terminator = *cur;
+
+ ++cur;
+
+ _cur_lexeme_contents.begin = cur;
+ while (*cur && *cur != terminator) cur++;
+ _cur_lexeme_contents.end = cur;
+
+ if (!*cur)
+ _cur_lexeme = lex_none;
+ else
+ {
+ cur += 1;
+ _cur_lexeme = lex_quoted_string;
+ }
+
+ break;
+ }
+
+ case ':':
+ if (*(cur+1) == ':')
+ {
+ cur += 2;
+ _cur_lexeme = lex_double_colon;
+ }
+ else
+ {
+ _cur_lexeme = lex_none;
+ }
+ break;
+
+ default:
+ if (PUGI__IS_CHARTYPEX(*cur, ctx_digit))
+ {
+ _cur_lexeme_contents.begin = cur;
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+
+ if (*cur == '.')
+ {
+ cur++;
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+ }
+
+ _cur_lexeme_contents.end = cur;
+
+ _cur_lexeme = lex_number;
+ }
+ else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
+ {
+ _cur_lexeme_contents.begin = cur;
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+
+ if (cur[0] == ':')
+ {
+ if (cur[1] == '*') // namespace test ncname:*
+ {
+ cur += 2; // :*
+ }
+ else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname
+ {
+ cur++; // :
+
+ while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+ }
+ }
+
+ _cur_lexeme_contents.end = cur;
+
+ _cur_lexeme = lex_string;
+ }
+ else
+ {
+ _cur_lexeme = lex_none;
+ }
+ }
+
+ _cur = cur;
+ }
+
+ lexeme_t current() const
+ {
+ return _cur_lexeme;
+ }
+
+ const char_t* current_pos() const
+ {
+ return _cur_lexeme_pos;
+ }
+
+ const xpath_lexer_string& contents() const
+ {
+ assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string);
+
+ return _cur_lexeme_contents;
+ }
+ };
+
+ enum ast_type_t
+ {
+ ast_op_or, // left or right
+ ast_op_and, // left and right
+ ast_op_equal, // left = right
+ ast_op_not_equal, // left != right
+ ast_op_less, // left < right
+ ast_op_greater, // left > right
+ ast_op_less_or_equal, // left <= right
+ ast_op_greater_or_equal, // left >= right
+ ast_op_add, // left + right
+ ast_op_subtract, // left - right
+ ast_op_multiply, // left * right
+ ast_op_divide, // left / right
+ ast_op_mod, // left % right
+ ast_op_negate, // left - right
+ ast_op_union, // left | right
+ ast_predicate, // apply predicate to set; next points to next predicate
+ ast_filter, // select * from left where right
+ ast_filter_posinv, // select * from left where right; proximity position invariant
+ ast_string_constant, // string constant
+ ast_number_constant, // number constant
+ ast_variable, // variable
+ ast_func_last, // last()
+ ast_func_position, // position()
+ ast_func_count, // count(left)
+ ast_func_id, // id(left)
+ ast_func_local_name_0, // local-name()
+ ast_func_local_name_1, // local-name(left)
+ ast_func_namespace_uri_0, // namespace-uri()
+ ast_func_namespace_uri_1, // namespace-uri(left)
+ ast_func_name_0, // name()
+ ast_func_name_1, // name(left)
+ ast_func_string_0, // string()
+ ast_func_string_1, // string(left)
+ ast_func_concat, // concat(left, right, siblings)
+ ast_func_starts_with, // starts_with(left, right)
+ ast_func_contains, // contains(left, right)
+ ast_func_substring_before, // substring-before(left, right)
+ ast_func_substring_after, // substring-after(left, right)
+ ast_func_substring_2, // substring(left, right)
+ ast_func_substring_3, // substring(left, right, third)
+ ast_func_string_length_0, // string-length()
+ ast_func_string_length_1, // string-length(left)
+ ast_func_normalize_space_0, // normalize-space()
+ ast_func_normalize_space_1, // normalize-space(left)
+ ast_func_translate, // translate(left, right, third)
+ ast_func_boolean, // boolean(left)
+ ast_func_not, // not(left)
+ ast_func_true, // true()
+ ast_func_false, // false()
+ ast_func_lang, // lang(left)
+ ast_func_number_0, // number()
+ ast_func_number_1, // number(left)
+ ast_func_sum, // sum(left)
+ ast_func_floor, // floor(left)
+ ast_func_ceiling, // ceiling(left)
+ ast_func_round, // round(left)
+ ast_step, // process set left with step
+ ast_step_root // select root node
+ };
+
+ enum axis_t
+ {
+ axis_ancestor,
+ axis_ancestor_or_self,
+ axis_attribute,
+ axis_child,
+ axis_descendant,
+ axis_descendant_or_self,
+ axis_following,
+ axis_following_sibling,
+ axis_namespace,
+ axis_parent,
+ axis_preceding,
+ axis_preceding_sibling,
+ axis_self
+ };
+
+ enum nodetest_t
+ {
+ nodetest_none,
+ nodetest_name,
+ nodetest_type_node,
+ nodetest_type_comment,
+ nodetest_type_pi,
+ nodetest_type_text,
+ nodetest_pi,
+ nodetest_all,
+ nodetest_all_in_namespace
+ };
+
+ template <axis_t N> struct axis_to_type
+ {
+ static const axis_t axis;
+ };
+
+ template <axis_t N> const axis_t axis_to_type<N>::axis = N;
+
+ class xpath_ast_node
+ {
+ private:
+ // node type
+ char _type;
+ char _rettype;
+
+ // for ast_step / ast_predicate
+ char _axis;
+ char _test;
+
+ // tree node structure
+ xpath_ast_node* _left;
+ xpath_ast_node* _right;
+ xpath_ast_node* _next;
+
+ union
+ {
+ // value for ast_string_constant
+ const char_t* string;
+ // value for ast_number_constant
+ double number;
+ // variable for ast_variable
+ xpath_variable* variable;
+ // node test for ast_step (node name/namespace/node type/pi target)
+ const char_t* nodetest;
+ } _data;
+
+ xpath_ast_node(const xpath_ast_node&);
+ xpath_ast_node& operator=(const xpath_ast_node&);
+
+ template <class Comp> static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)
+ {
+ xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
+
+ if (lt != xpath_type_node_set && rt != xpath_type_node_set)
+ {
+ if (lt == xpath_type_boolean || rt == xpath_type_boolean)
+ return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));
+ else if (lt == xpath_type_number || rt == xpath_type_number)
+ return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));
+ else if (lt == xpath_type_string || rt == xpath_type_string)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_string ls = lhs->eval_string(c, stack);
+ xpath_string rs = rhs->eval_string(c, stack);
+
+ return comp(ls, rs);
+ }
+ }
+ else if (lt == xpath_type_node_set && rt == xpath_type_node_set)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+
+ for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
+ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ if (comp(string_value(*li, stack.result), string_value(*ri, stack.result)))
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ if (lt == xpath_type_node_set)
+ {
+ swap(lhs, rhs);
+ swap(lt, rt);
+ }
+
+ if (lt == xpath_type_boolean)
+ return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));
+ else if (lt == xpath_type_number)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ double l = lhs->eval_number(c, stack);
+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+
+ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
+ return true;
+ }
+
+ return false;
+ }
+ else if (lt == xpath_type_string)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_string l = lhs->eval_string(c, stack);
+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+
+ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ if (comp(l, string_value(*ri, stack.result)))
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ assert(!"Wrong types");
+ return false;
+ }
+
+ template <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)
+ {
+ xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
+
+ if (lt != xpath_type_node_set && rt != xpath_type_node_set)
+ return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));
+ else if (lt == xpath_type_node_set && rt == xpath_type_node_set)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+
+ for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ double l = convert_string_to_number(string_value(*li, stack.result).c_str());
+
+ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+ {
+ xpath_allocator_capture crii(stack.result);
+
+ if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
+ return true;
+ }
+ }
+
+ return false;
+ }
+ else if (lt != xpath_type_node_set && rt == xpath_type_node_set)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ double l = lhs->eval_number(c, stack);
+ xpath_node_set_raw rs = rhs->eval_node_set(c, stack);
+
+ for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
+ return true;
+ }
+
+ return false;
+ }
+ else if (lt == xpath_type_node_set && rt != xpath_type_node_set)
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_node_set_raw ls = lhs->eval_node_set(c, stack);
+ double r = rhs->eval_number(c, stack);
+
+ for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r))
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ assert(!"Wrong types");
+ return false;
+ }
+ }
+
+ void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
+ {
+ assert(ns.size() >= first);
+
+ size_t i = 1;
+ size_t size = ns.size() - first;
+
+ xpath_node* last = ns.begin() + first;
+
+ // remove_if... or well, sort of
+ for (xpath_node* it = last; it != ns.end(); ++it, ++i)
+ {
+ xpath_context c(*it, i, size);
+
+ if (expr->rettype() == xpath_type_number)
+ {
+ if (expr->eval_number(c, stack) == i)
+ *last++ = *it;
+ }
+ else if (expr->eval_boolean(c, stack))
+ *last++ = *it;
+ }
+
+ ns.truncate(last);
+ }
+
+ void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack)
+ {
+ if (ns.size() == first) return;
+
+ for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
+ {
+ apply_predicate(ns, first, pred->_left, stack);
+ }
+ }
+
+ void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc)
+ {
+ if (!a) return;
+
+ const char_t* name = a.name();
+
+ // There are no attribute nodes corresponding to attributes that declare namespaces
+ // That is, "xmlns:..." or "xmlns"
+ if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return;
+
+ switch (_test)
+ {
+ case nodetest_name:
+ if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc);
+ break;
+
+ case nodetest_type_node:
+ case nodetest_all:
+ ns.push_back(xpath_node(a, parent), alloc);
+ break;
+
+ case nodetest_all_in_namespace:
+ if (starts_with(name, _data.nodetest))
+ ns.push_back(xpath_node(a, parent), alloc);
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc)
+ {
+ if (!n) return;
+
+ switch (_test)
+ {
+ case nodetest_name:
+ if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc);
+ break;
+
+ case nodetest_type_node:
+ ns.push_back(n, alloc);
+ break;
+
+ case nodetest_type_comment:
+ if (n.type() == node_comment)
+ ns.push_back(n, alloc);
+ break;
+
+ case nodetest_type_text:
+ if (n.type() == node_pcdata || n.type() == node_cdata)
+ ns.push_back(n, alloc);
+ break;
+
+ case nodetest_type_pi:
+ if (n.type() == node_pi)
+ ns.push_back(n, alloc);
+ break;
+
+ case nodetest_pi:
+ if (n.type() == node_pi && strequal(n.name(), _data.nodetest))
+ ns.push_back(n, alloc);
+ break;
+
+ case nodetest_all:
+ if (n.type() == node_element)
+ ns.push_back(n, alloc);
+ break;
+
+ case nodetest_all_in_namespace:
+ if (n.type() == node_element && starts_with(n.name(), _data.nodetest))
+ ns.push_back(n, alloc);
+ break;
+
+ default:
+ assert(!"Unknown axis");
+ }
+ }
+
+ template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T)
+ {
+ const axis_t axis = T::axis;
+
+ switch (axis)
+ {
+ case axis_attribute:
+ {
+ for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute())
+ step_push(ns, a, n, alloc);
+
+ break;
+ }
+
+ case axis_child:
+ {
+ for (xml_node c = n.first_child(); c; c = c.next_sibling())
+ step_push(ns, c, alloc);
+
+ break;
+ }
+
+ case axis_descendant:
+ case axis_descendant_or_self:
+ {
+ if (axis == axis_descendant_or_self)
+ step_push(ns, n, alloc);
+
+ xml_node cur = n.first_child();
+
+ while (cur && cur != n)
+ {
+ step_push(ns, cur, alloc);
+
+ if (cur.first_child())
+ cur = cur.first_child();
+ else if (cur.next_sibling())
+ cur = cur.next_sibling();
+ else
+ {
+ while (!cur.next_sibling() && cur != n)
+ cur = cur.parent();
+
+ if (cur != n) cur = cur.next_sibling();
+ }
+ }
+
+ break;
+ }
+
+ case axis_following_sibling:
+ {
+ for (xml_node c = n.next_sibling(); c; c = c.next_sibling())
+ step_push(ns, c, alloc);
+
+ break;
+ }
+
+ case axis_preceding_sibling:
+ {
+ for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling())
+ step_push(ns, c, alloc);
+
+ break;
+ }
+
+ case axis_following:
+ {
+ xml_node cur = n;
+
+ // exit from this node so that we don't include descendants
+ while (cur && !cur.next_sibling()) cur = cur.parent();
+ cur = cur.next_sibling();
+
+ for (;;)
+ {
+ step_push(ns, cur, alloc);
+
+ if (cur.first_child())
+ cur = cur.first_child();
+ else if (cur.next_sibling())
+ cur = cur.next_sibling();
+ else
+ {
+ while (cur && !cur.next_sibling()) cur = cur.parent();
+ cur = cur.next_sibling();
+
+ if (!cur) break;
+ }
+ }
+
+ break;
+ }
+
+ case axis_preceding:
+ {
+ xml_node cur = n;
+
+ while (cur && !cur.previous_sibling()) cur = cur.parent();
+ cur = cur.previous_sibling();
+
+ for (;;)
+ {
+ if (cur.last_child())
+ cur = cur.last_child();
+ else
+ {
+ // leaf node, can't be ancestor
+ step_push(ns, cur, alloc);
+
+ if (cur.previous_sibling())
+ cur = cur.previous_sibling();
+ else
+ {
+ do
+ {
+ cur = cur.parent();
+ if (!cur) break;
+
+ if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc);
+ }
+ while (!cur.previous_sibling());
+
+ cur = cur.previous_sibling();
+
+ if (!cur) break;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case axis_ancestor:
+ case axis_ancestor_or_self:
+ {
+ if (axis == axis_ancestor_or_self)
+ step_push(ns, n, alloc);
+
+ xml_node cur = n.parent();
+
+ while (cur)
+ {
+ step_push(ns, cur, alloc);
+
+ cur = cur.parent();
+ }
+
+ break;
+ }
+
+ case axis_self:
+ {
+ step_push(ns, n, alloc);
+
+ break;
+ }
+
+ case axis_parent:
+ {
+ if (n.parent()) step_push(ns, n.parent(), alloc);
+
+ break;
+ }
+
+ default:
+ assert(!"Unimplemented axis");
+ }
+ }
+
+ template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, T v)
+ {
+ const axis_t axis = T::axis;
+
+ switch (axis)
+ {
+ case axis_ancestor:
+ case axis_ancestor_or_self:
+ {
+ if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test
+ step_push(ns, a, p, alloc);
+
+ xml_node cur = p;
+
+ while (cur)
+ {
+ step_push(ns, cur, alloc);
+
+ cur = cur.parent();
+ }
+
+ break;
+ }
+
+ case axis_descendant_or_self:
+ case axis_self:
+ {
+ if (_test == nodetest_type_node) // reject attributes based on principal node type test
+ step_push(ns, a, p, alloc);
+
+ break;
+ }
+
+ case axis_following:
+ {
+ xml_node cur = p;
+
+ for (;;)
+ {
+ if (cur.first_child())
+ cur = cur.first_child();
+ else if (cur.next_sibling())
+ cur = cur.next_sibling();
+ else
+ {
+ while (cur && !cur.next_sibling()) cur = cur.parent();
+ cur = cur.next_sibling();
+
+ if (!cur) break;
+ }
+
+ step_push(ns, cur, alloc);
+ }
+
+ break;
+ }
+
+ case axis_parent:
+ {
+ step_push(ns, p, alloc);
+
+ break;
+ }
+
+ case axis_preceding:
+ {
+ // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding
+ step_fill(ns, p, alloc, v);
+ break;
+ }
+
+ default:
+ assert(!"Unimplemented axis");
+ }
+ }
+
+ template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v)
+ {
+ const axis_t axis = T::axis;
+ bool attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
+
+ xpath_node_set_raw ns;
+ ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
+
+ if (_left)
+ {
+ xpath_node_set_raw s = _left->eval_node_set(c, stack);
+
+ // self axis preserves the original order
+ if (axis == axis_self) ns.set_type(s.type());
+
+ for (const xpath_node* it = s.begin(); it != s.end(); ++it)
+ {
+ size_t size = ns.size();
+
+ // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes
+ if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
+
+ if (it->node())
+ step_fill(ns, it->node(), stack.result, v);
+ else if (attributes)
+ step_fill(ns, it->attribute(), it->parent(), stack.result, v);
+
+ apply_predicates(ns, size, stack);
+ }
+ }
+ else
+ {
+ if (c.n.node())
+ step_fill(ns, c.n.node(), stack.result, v);
+ else if (attributes)
+ step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v);
+
+ apply_predicates(ns, 0, stack);
+ }
+
+ // child, attribute and self axes always generate unique set of nodes
+ // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice
+ if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted)
+ ns.remove_duplicates();
+
+ return ns;
+ }
+
+ public:
+ xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value):
+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
+ {
+ assert(type == ast_string_constant);
+ _data.string = value;
+ }
+
+ xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value):
+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
+ {
+ assert(type == ast_number_constant);
+ _data.number = value;
+ }
+
+ xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value):
+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
+ {
+ assert(type == ast_variable);
+ _data.variable = value;
+ }
+
+ xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0):
+ _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0)
+ {
+ }
+
+ xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):
+ _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
+ {
+ _data.nodetest = contents;
+ }
+
+ void set_next(xpath_ast_node* value)
+ {
+ _next = value;
+ }
+
+ void set_right(xpath_ast_node* value)
+ {
+ _right = value;
+ }
+
+ bool eval_boolean(const xpath_context& c, const xpath_stack& stack)
+ {
+ switch (_type)
+ {
+ case ast_op_or:
+ return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack);
+
+ case ast_op_and:
+ return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack);
+
+ case ast_op_equal:
+ return compare_eq(_left, _right, c, stack, equal_to());
+
+ case ast_op_not_equal:
+ return compare_eq(_left, _right, c, stack, not_equal_to());
+
+ case ast_op_less:
+ return compare_rel(_left, _right, c, stack, less());
+
+ case ast_op_greater:
+ return compare_rel(_right, _left, c, stack, less());
+
+ case ast_op_less_or_equal:
+ return compare_rel(_left, _right, c, stack, less_equal());
+
+ case ast_op_greater_or_equal:
+ return compare_rel(_right, _left, c, stack, less_equal());
+
+ case ast_func_starts_with:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_string lr = _left->eval_string(c, stack);
+ xpath_string rr = _right->eval_string(c, stack);
+
+ return starts_with(lr.c_str(), rr.c_str());
+ }
+
+ case ast_func_contains:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_string lr = _left->eval_string(c, stack);
+ xpath_string rr = _right->eval_string(c, stack);
+
+ return find_substring(lr.c_str(), rr.c_str()) != 0;
+ }
+
+ case ast_func_boolean:
+ return _left->eval_boolean(c, stack);
+
+ case ast_func_not:
+ return !_left->eval_boolean(c, stack);
+
+ case ast_func_true:
+ return true;
+
+ case ast_func_false:
+ return false;
+
+ case ast_func_lang:
+ {
+ if (c.n.attribute()) return false;
+
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_string lang = _left->eval_string(c, stack);
+
+ for (xml_node n = c.n.node(); n; n = n.parent())
+ {
+ xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang"));
+
+ if (a)
+ {
+ const char_t* value = a.value();
+
+ // strnicmp / strncasecmp is not portable
+ for (const char_t* lit = lang.c_str(); *lit; ++lit)
+ {
+ if (tolower_ascii(*lit) != tolower_ascii(*value)) return false;
+ ++value;
+ }
+
+ return *value == 0 || *value == '-';
+ }
+ }
+
+ return false;
+ }
+
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_boolean)
+ return _data.variable->get_boolean();
+
+ // fallthrough to type conversion
+ }
+
+ default:
+ {
+ switch (_rettype)
+ {
+ case xpath_type_number:
+ return convert_number_to_boolean(eval_number(c, stack));
+
+ case xpath_type_string:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return !eval_string(c, stack).empty();
+ }
+
+ case xpath_type_node_set:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return !eval_node_set(c, stack).empty();
+ }
+
+ default:
+ assert(!"Wrong expression for return type boolean");
+ return false;
+ }
+ }
+ }
+ }
+
+ double eval_number(const xpath_context& c, const xpath_stack& stack)
+ {
+ switch (_type)
+ {
+ case ast_op_add:
+ return _left->eval_number(c, stack) + _right->eval_number(c, stack);
+
+ case ast_op_subtract:
+ return _left->eval_number(c, stack) - _right->eval_number(c, stack);
+
+ case ast_op_multiply:
+ return _left->eval_number(c, stack) * _right->eval_number(c, stack);
+
+ case ast_op_divide:
+ return _left->eval_number(c, stack) / _right->eval_number(c, stack);
+
+ case ast_op_mod:
+ return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack));
+
+ case ast_op_negate:
+ return -_left->eval_number(c, stack);
+
+ case ast_number_constant:
+ return _data.number;
+
+ case ast_func_last:
+ return static_cast<double>(c.size);
+
+ case ast_func_position:
+ return static_cast<double>(c.position);
+
+ case ast_func_count:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return static_cast<double>(_left->eval_node_set(c, stack).size());
+ }
+
+ case ast_func_string_length_0:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return static_cast<double>(string_value(c.n, stack.result).length());
+ }
+
+ case ast_func_string_length_1:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return static_cast<double>(_left->eval_string(c, stack).length());
+ }
+
+ case ast_func_number_0:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return convert_string_to_number(string_value(c.n, stack.result).c_str());
+ }
+
+ case ast_func_number_1:
+ return _left->eval_number(c, stack);
+
+ case ast_func_sum:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ double r = 0;
+
+ xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+
+ for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)
+ {
+ xpath_allocator_capture cri(stack.result);
+
+ r += convert_string_to_number(string_value(*it, stack.result).c_str());
+ }
+
+ return r;
+ }
+
+ case ast_func_floor:
+ {
+ double r = _left->eval_number(c, stack);
+
+ return r == r ? floor(r) : r;
+ }
+
+ case ast_func_ceiling:
+ {
+ double r = _left->eval_number(c, stack);
+
+ return r == r ? ceil(r) : r;
+ }
+
+ case ast_func_round:
+ return round_nearest_nzero(_left->eval_number(c, stack));
+
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_number)
+ return _data.variable->get_number();
+
+ // fallthrough to type conversion
+ }
+
+ default:
+ {
+ switch (_rettype)
+ {
+ case xpath_type_boolean:
+ return eval_boolean(c, stack) ? 1 : 0;
+
+ case xpath_type_string:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return convert_string_to_number(eval_string(c, stack).c_str());
+ }
+
+ case xpath_type_node_set:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ return convert_string_to_number(eval_string(c, stack).c_str());
+ }
+
+ default:
+ assert(!"Wrong expression for return type number");
+ return 0;
+ }
+
+ }
+ }
+ }
+
+ xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack)
+ {
+ assert(_type == ast_func_concat);
+
+ xpath_allocator_capture ct(stack.temp);
+
+ // count the string number
+ size_t count = 1;
+ for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++;
+
+ // gather all strings
+ xpath_string static_buffer[4];
+ xpath_string* buffer = static_buffer;
+
+ // allocate on-heap for large concats
+ if (count > sizeof(static_buffer) / sizeof(static_buffer[0]))
+ {
+ buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string)));
+ assert(buffer);
+ }
+
+ // evaluate all strings to temporary stack
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ buffer[0] = _left->eval_string(c, swapped_stack);
+
+ size_t pos = 1;
+ for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack);
+ assert(pos == count);
+
+ // get total length
+ size_t length = 0;
+ for (size_t i = 0; i < count; ++i) length += buffer[i].length();
+
+ // create final string
+ char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t)));
+ assert(result);
+
+ char_t* ri = result;
+
+ for (size_t j = 0; j < count; ++j)
+ for (const char_t* bi = buffer[j].c_str(); *bi; ++bi)
+ *ri++ = *bi;
+
+ *ri = 0;
+
+ return xpath_string(result, true);
+ }
+
+ xpath_string eval_string(const xpath_context& c, const xpath_stack& stack)
+ {
+ switch (_type)
+ {
+ case ast_string_constant:
+ return xpath_string_const(_data.string);
+
+ case ast_func_local_name_0:
+ {
+ xpath_node na = c.n;
+
+ return xpath_string_const(local_name(na));
+ }
+
+ case ast_func_local_name_1:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+ xpath_node na = ns.first();
+
+ return xpath_string_const(local_name(na));
+ }
+
+ case ast_func_name_0:
+ {
+ xpath_node na = c.n;
+
+ return xpath_string_const(qualified_name(na));
+ }
+
+ case ast_func_name_1:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+ xpath_node na = ns.first();
+
+ return xpath_string_const(qualified_name(na));
+ }
+
+ case ast_func_namespace_uri_0:
+ {
+ xpath_node na = c.n;
+
+ return xpath_string_const(namespace_uri(na));
+ }
+
+ case ast_func_namespace_uri_1:
+ {
+ xpath_allocator_capture cr(stack.result);
+
+ xpath_node_set_raw ns = _left->eval_node_set(c, stack);
+ xpath_node na = ns.first();
+
+ return xpath_string_const(namespace_uri(na));
+ }
+
+ case ast_func_string_0:
+ return string_value(c.n, stack.result);
+
+ case ast_func_string_1:
+ return _left->eval_string(c, stack);
+
+ case ast_func_concat:
+ return eval_string_concat(c, stack);
+
+ case ast_func_substring_before:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_string s = _left->eval_string(c, swapped_stack);
+ xpath_string p = _right->eval_string(c, swapped_stack);
+
+ const char_t* pos = find_substring(s.c_str(), p.c_str());
+
+ return pos ? xpath_string(s.c_str(), pos, stack.result) : xpath_string();
+ }
+
+ case ast_func_substring_after:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_string s = _left->eval_string(c, swapped_stack);
+ xpath_string p = _right->eval_string(c, swapped_stack);
+
+ const char_t* pos = find_substring(s.c_str(), p.c_str());
+ if (!pos) return xpath_string();
+
+ const char_t* result = pos + p.length();
+
+ return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result);
+ }
+
+ case ast_func_substring_2:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_string s = _left->eval_string(c, swapped_stack);
+ size_t s_length = s.length();
+
+ double first = round_nearest(_right->eval_number(c, stack));
+
+ if (is_nan(first)) return xpath_string(); // NaN
+ else if (first >= s_length + 1) return xpath_string();
+
+ size_t pos = first < 1 ? 1 : static_cast<size_t>(first);
+ assert(1 <= pos && pos <= s_length + 1);
+
+ const char_t* rbegin = s.c_str() + (pos - 1);
+
+ return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_const(rbegin);
+ }
+
+ case ast_func_substring_3:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_string s = _left->eval_string(c, swapped_stack);
+ size_t s_length = s.length();
+
+ double first = round_nearest(_right->eval_number(c, stack));
+ double last = first + round_nearest(_right->_next->eval_number(c, stack));
+
+ if (is_nan(first) || is_nan(last)) return xpath_string();
+ else if (first >= s_length + 1) return xpath_string();
+ else if (first >= last) return xpath_string();
+ else if (last < 1) return xpath_string();
+
+ size_t pos = first < 1 ? 1 : static_cast<size_t>(first);
+ size_t end = last >= s_length + 1 ? s_length + 1 : static_cast<size_t>(last);
+
+ assert(1 <= pos && pos <= end && end <= s_length + 1);
+ const char_t* rbegin = s.c_str() + (pos - 1);
+ const char_t* rend = s.c_str() + (end - 1);
+
+ return (end == s_length + 1 && !s.uses_heap()) ? xpath_string_const(rbegin) : xpath_string(rbegin, rend, stack.result);
+ }
+
+ case ast_func_normalize_space_0:
+ {
+ xpath_string s = string_value(c.n, stack.result);
+
+ normalize_space(s.data(stack.result));
+
+ return s;
+ }
+
+ case ast_func_normalize_space_1:
+ {
+ xpath_string s = _left->eval_string(c, stack);
+
+ normalize_space(s.data(stack.result));
+
+ return s;
+ }
+
+ case ast_func_translate:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_string s = _left->eval_string(c, stack);
+ xpath_string from = _right->eval_string(c, swapped_stack);
+ xpath_string to = _right->_next->eval_string(c, swapped_stack);
+
+ translate(s.data(stack.result), from.c_str(), to.c_str());
+
+ return s;
+ }
+
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_string)
+ return xpath_string_const(_data.variable->get_string());
+
+ // fallthrough to type conversion
+ }
+
+ default:
+ {
+ switch (_rettype)
+ {
+ case xpath_type_boolean:
+ return xpath_string_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
+
+ case xpath_type_number:
+ return convert_number_to_string(eval_number(c, stack), stack.result);
+
+ case xpath_type_node_set:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_node_set_raw ns = eval_node_set(c, swapped_stack);
+ return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
+ }
+
+ default:
+ assert(!"Wrong expression for return type string");
+ return xpath_string();
+ }
+ }
+ }
+ }
+
+ xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack)
+ {
+ switch (_type)
+ {
+ case ast_op_union:
+ {
+ xpath_allocator_capture cr(stack.temp);
+
+ xpath_stack swapped_stack = {stack.temp, stack.result};
+
+ xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack);
+ xpath_node_set_raw rs = _right->eval_node_set(c, stack);
+
+ // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
+ rs.set_type(xpath_node_set::type_unsorted);
+
+ rs.append(ls.begin(), ls.end(), stack.result);
+ rs.remove_duplicates();
+
+ return rs;
+ }
+
+ case ast_filter:
+ case ast_filter_posinv:
+ {
+ xpath_node_set_raw set = _left->eval_node_set(c, stack);
+
+ // either expression is a number or it contains position() call; sort by document order
+ if (_type == ast_filter) set.sort_do();
+
+ apply_predicate(set, 0, _right, stack);
+
+ return set;
+ }
+
+ case ast_func_id:
+ return xpath_node_set_raw();
+
+ case ast_step:
+ {
+ switch (_axis)
+ {
+ case axis_ancestor:
+ return step_do(c, stack, axis_to_type<axis_ancestor>());
+
+ case axis_ancestor_or_self:
+ return step_do(c, stack, axis_to_type<axis_ancestor_or_self>());
+
+ case axis_attribute:
+ return step_do(c, stack, axis_to_type<axis_attribute>());
+
+ case axis_child:
+ return step_do(c, stack, axis_to_type<axis_child>());
+
+ case axis_descendant:
+ return step_do(c, stack, axis_to_type<axis_descendant>());
+
+ case axis_descendant_or_self:
+ return step_do(c, stack, axis_to_type<axis_descendant_or_self>());
+
+ case axis_following:
+ return step_do(c, stack, axis_to_type<axis_following>());
+
+ case axis_following_sibling:
+ return step_do(c, stack, axis_to_type<axis_following_sibling>());
+
+ case axis_namespace:
+ // namespaced axis is not supported
+ return xpath_node_set_raw();
+
+ case axis_parent:
+ return step_do(c, stack, axis_to_type<axis_parent>());
+
+ case axis_preceding:
+ return step_do(c, stack, axis_to_type<axis_preceding>());
+
+ case axis_preceding_sibling:
+ return step_do(c, stack, axis_to_type<axis_preceding_sibling>());
+
+ case axis_self:
+ return step_do(c, stack, axis_to_type<axis_self>());
+
+ default:
+ assert(!"Unknown axis");
+ return xpath_node_set_raw();
+ }
+ }
+
+ case ast_step_root:
+ {
+ assert(!_right); // root step can't have any predicates
+
+ xpath_node_set_raw ns;
+
+ ns.set_type(xpath_node_set::type_sorted);
+
+ if (c.n.node()) ns.push_back(c.n.node().root(), stack.result);
+ else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result);
+
+ return ns;
+ }
+
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_node_set)
+ {
+ const xpath_node_set& s = _data.variable->get_node_set();
+
+ xpath_node_set_raw ns;
+
+ ns.set_type(s.type());
+ ns.append(s.begin(), s.end(), stack.result);
+
+ return ns;
+ }
+
+ // fallthrough to type conversion
+ }
+
+ default:
+ assert(!"Wrong expression for return type node set");
+ return xpath_node_set_raw();
+ }
+ }
+
+ bool is_posinv()
+ {
+ switch (_type)
+ {
+ case ast_func_position:
+ return false;
+
+ case ast_string_constant:
+ case ast_number_constant:
+ case ast_variable:
+ return true;
+
+ case ast_step:
+ case ast_step_root:
+ return true;
+
+ case ast_predicate:
+ case ast_filter:
+ case ast_filter_posinv:
+ return true;
+
+ default:
+ if (_left && !_left->is_posinv()) return false;
+
+ for (xpath_ast_node* n = _right; n; n = n->_next)
+ if (!n->is_posinv()) return false;
+
+ return true;
+ }
+ }
+
+ xpath_value_type rettype() const
+ {
+ return static_cast<xpath_value_type>(_rettype);
+ }
+ };
+
+ struct xpath_parser
+ {
+ xpath_allocator* _alloc;
+ xpath_lexer _lexer;
+
+ const char_t* _query;
+ xpath_variable_set* _variables;
+
+ xpath_parse_result* _result;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ jmp_buf _error_handler;
+ #endif
+
+ void throw_error(const char* message)
+ {
+ _result->error = message;
+ _result->offset = _lexer.current_pos() - _query;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ longjmp(_error_handler, 1);
+ #else
+ throw xpath_exception(*_result);
+ #endif
+ }
+
+ void throw_error_oom()
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ throw_error("Out of memory");
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
+ void* alloc_node()
+ {
+ void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node));
+
+ if (!result) throw_error_oom();
+
+ return result;
+ }
+
+ const char_t* alloc_string(const xpath_lexer_string& value)
+ {
+ if (value.begin)
+ {
+ size_t length = static_cast<size_t>(value.end - value.begin);
+
+ char_t* c = static_cast<char_t*>(_alloc->allocate_nothrow((length + 1) * sizeof(char_t)));
+ if (!c) throw_error_oom();
+
+ memcpy(c, value.begin, length * sizeof(char_t));
+ c[length] = 0;
+
+ return c;
+ }
+ else return 0;
+ }
+
+ xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2])
+ {
+ assert(argc <= 1);
+
+ if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");
+
+ return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]);
+ }
+
+ xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2])
+ {
+ switch (name.begin[0])
+ {
+ case 'b':
+ if (name == PUGIXML_TEXT("boolean") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_boolean, xpath_type_boolean, args[0]);
+
+ break;
+
+ case 'c':
+ if (name == PUGIXML_TEXT("count") && argc == 1)
+ {
+ if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");
+ return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]);
+ }
+ else if (name == PUGIXML_TEXT("contains") && argc == 2)
+ return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("concat") && argc >= 2)
+ return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("ceiling") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]);
+
+ break;
+
+ case 'f':
+ if (name == PUGIXML_TEXT("false") && argc == 0)
+ return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean);
+ else if (name == PUGIXML_TEXT("floor") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]);
+
+ break;
+
+ case 'i':
+ if (name == PUGIXML_TEXT("id") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]);
+
+ break;
+
+ case 'l':
+ if (name == PUGIXML_TEXT("last") && argc == 0)
+ return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number);
+ else if (name == PUGIXML_TEXT("lang") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]);
+ else if (name == PUGIXML_TEXT("local-name") && argc <= 1)
+ return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args);
+
+ break;
+
+ case 'n':
+ if (name == PUGIXML_TEXT("name") && argc <= 1)
+ return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args);
+ else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1)
+ return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args);
+ else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1)
+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("not") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]);
+ else if (name == PUGIXML_TEXT("number") && argc <= 1)
+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);
+
+ break;
+
+ case 'p':
+ if (name == PUGIXML_TEXT("position") && argc == 0)
+ return new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number);
+
+ break;
+
+ case 'r':
+ if (name == PUGIXML_TEXT("round") && argc == 1)
+ return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]);
+
+ break;
+
+ case 's':
+ if (name == PUGIXML_TEXT("string") && argc <= 1)
+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);
+ else if (name == PUGIXML_TEXT("string-length") && argc <= 1)
+ return new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_string, args[0]);
+ else if (name == PUGIXML_TEXT("starts-with") && argc == 2)
+ return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("substring-before") && argc == 2)
+ return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("substring-after") && argc == 2)
+ return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3))
+ return new (alloc_node()) xpath_ast_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("sum") && argc == 1)
+ {
+ if (args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set");
+ return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]);
+ }
+
+ break;
+
+ case 't':
+ if (name == PUGIXML_TEXT("translate") && argc == 3)
+ return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]);
+ else if (name == PUGIXML_TEXT("true") && argc == 0)
+ return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean);
+
+ break;
+
+ default:
+ break;
+ }
+
+ throw_error("Unrecognized function or wrong parameter count");
+
+ return 0;
+ }
+
+ axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified)
+ {
+ specified = true;
+
+ switch (name.begin[0])
+ {
+ case 'a':
+ if (name == PUGIXML_TEXT("ancestor"))
+ return axis_ancestor;
+ else if (name == PUGIXML_TEXT("ancestor-or-self"))
+ return axis_ancestor_or_self;
+ else if (name == PUGIXML_TEXT("attribute"))
+ return axis_attribute;
+
+ break;
+
+ case 'c':
+ if (name == PUGIXML_TEXT("child"))
+ return axis_child;
+
+ break;
+
+ case 'd':
+ if (name == PUGIXML_TEXT("descendant"))
+ return axis_descendant;
+ else if (name == PUGIXML_TEXT("descendant-or-self"))
+ return axis_descendant_or_self;
+
+ break;
+
+ case 'f':
+ if (name == PUGIXML_TEXT("following"))
+ return axis_following;
+ else if (name == PUGIXML_TEXT("following-sibling"))
+ return axis_following_sibling;
+
+ break;
+
+ case 'n':
+ if (name == PUGIXML_TEXT("namespace"))
+ return axis_namespace;
+
+ break;
+
+ case 'p':
+ if (name == PUGIXML_TEXT("parent"))
+ return axis_parent;
+ else if (name == PUGIXML_TEXT("preceding"))
+ return axis_preceding;
+ else if (name == PUGIXML_TEXT("preceding-sibling"))
+ return axis_preceding_sibling;
+
+ break;
+
+ case 's':
+ if (name == PUGIXML_TEXT("self"))
+ return axis_self;
+
+ break;
+
+ default:
+ break;
+ }
+
+ specified = false;
+ return axis_child;
+ }
+
+ nodetest_t parse_node_test_type(const xpath_lexer_string& name)
+ {
+ switch (name.begin[0])
+ {
+ case 'c':
+ if (name == PUGIXML_TEXT("comment"))
+ return nodetest_type_comment;
+
+ break;
+
+ case 'n':
+ if (name == PUGIXML_TEXT("node"))
+ return nodetest_type_node;
+
+ break;
+
+ case 'p':
+ if (name == PUGIXML_TEXT("processing-instruction"))
+ return nodetest_type_pi;
+
+ break;
+
+ case 't':
+ if (name == PUGIXML_TEXT("text"))
+ return nodetest_type_text;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return nodetest_none;
+ }
+
+ // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
+ xpath_ast_node* parse_primary_expression()
+ {
+ switch (_lexer.current())
+ {
+ case lex_var_ref:
+ {
+ xpath_lexer_string name = _lexer.contents();
+
+ if (!_variables)
+ throw_error("Unknown variable: variable set is not provided");
+
+ xpath_variable* var = get_variable(_variables, name.begin, name.end);
+
+ if (!var)
+ throw_error("Unknown variable: variable set does not contain the given name");
+
+ _lexer.next();
+
+ return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var);
+ }
+
+ case lex_open_brace:
+ {
+ _lexer.next();
+
+ xpath_ast_node* n = parse_expression();
+
+ if (_lexer.current() != lex_close_brace)
+ throw_error("Unmatched braces");
+
+ _lexer.next();
+
+ return n;
+ }
+
+ case lex_quoted_string:
+ {
+ const char_t* value = alloc_string(_lexer.contents());
+
+ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value);
+ _lexer.next();
+
+ return n;
+ }
+
+ case lex_number:
+ {
+ double value = 0;
+
+ if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value))
+ throw_error_oom();
+
+ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value);
+ _lexer.next();
+
+ return n;
+ }
+
+ case lex_string:
+ {
+ xpath_ast_node* args[2] = {0};
+ size_t argc = 0;
+
+ xpath_lexer_string function = _lexer.contents();
+ _lexer.next();
+
+ xpath_ast_node* last_arg = 0;
+
+ if (_lexer.current() != lex_open_brace)
+ throw_error("Unrecognized function call");
+ _lexer.next();
+
+ if (_lexer.current() != lex_close_brace)
+ args[argc++] = parse_expression();
+
+ while (_lexer.current() != lex_close_brace)
+ {
+ if (_lexer.current() != lex_comma)
+ throw_error("No comma between function arguments");
+ _lexer.next();
+
+ xpath_ast_node* n = parse_expression();
+
+ if (argc < 2) args[argc] = n;
+ else last_arg->set_next(n);
+
+ argc++;
+ last_arg = n;
+ }
+
+ _lexer.next();
+
+ return parse_function(function, argc, args);
+ }
+
+ default:
+ throw_error("Unrecognizable primary expression");
+
+ return 0;
+ }
+ }
+
+ // FilterExpr ::= PrimaryExpr | FilterExpr Predicate
+ // Predicate ::= '[' PredicateExpr ']'
+ // PredicateExpr ::= Expr
+ xpath_ast_node* parse_filter_expression()
+ {
+ xpath_ast_node* n = parse_primary_expression();
+
+ while (_lexer.current() == lex_open_square_brace)
+ {
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_expression();
+
+ if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set");
+
+ bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv();
+
+ n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr);
+
+ if (_lexer.current() != lex_close_square_brace)
+ throw_error("Unmatched square brace");
+
+ _lexer.next();
+ }
+
+ return n;
+ }
+
+ // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
+ // AxisSpecifier ::= AxisName '::' | '@'?
+ // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'
+ // NameTest ::= '*' | NCName ':' '*' | QName
+ // AbbreviatedStep ::= '.' | '..'
+ xpath_ast_node* parse_step(xpath_ast_node* set)
+ {
+ if (set && set->rettype() != xpath_type_node_set)
+ throw_error("Step has to be applied to node set");
+
+ bool axis_specified = false;
+ axis_t axis = axis_child; // implied child axis
+
+ if (_lexer.current() == lex_axis_attribute)
+ {
+ axis = axis_attribute;
+ axis_specified = true;
+
+ _lexer.next();
+ }
+ else if (_lexer.current() == lex_dot)
+ {
+ _lexer.next();
+
+ return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0);
+ }
+ else if (_lexer.current() == lex_double_dot)
+ {
+ _lexer.next();
+
+ return new (alloc_node()) xpath_ast_node(ast_step, set, axis_parent, nodetest_type_node, 0);
+ }
+
+ nodetest_t nt_type = nodetest_none;
+ xpath_lexer_string nt_name;
+
+ if (_lexer.current() == lex_string)
+ {
+ // node name test
+ nt_name = _lexer.contents();
+ _lexer.next();
+
+ // was it an axis name?
+ if (_lexer.current() == lex_double_colon)
+ {
+ // parse axis name
+ if (axis_specified) throw_error("Two axis specifiers in one step");
+
+ axis = parse_axis_name(nt_name, axis_specified);
+
+ if (!axis_specified) throw_error("Unknown axis");
+
+ // read actual node test
+ _lexer.next();
+
+ if (_lexer.current() == lex_multiply)
+ {
+ nt_type = nodetest_all;
+ nt_name = xpath_lexer_string();
+ _lexer.next();
+ }
+ else if (_lexer.current() == lex_string)
+ {
+ nt_name = _lexer.contents();
+ _lexer.next();
+ }
+ else throw_error("Unrecognized node test");
+ }
+
+ if (nt_type == nodetest_none)
+ {
+ // node type test or processing-instruction
+ if (_lexer.current() == lex_open_brace)
+ {
+ _lexer.next();
+
+ if (_lexer.current() == lex_close_brace)
+ {
+ _lexer.next();
+
+ nt_type = parse_node_test_type(nt_name);
+
+ if (nt_type == nodetest_none) throw_error("Unrecognized node type");
+
+ nt_name = xpath_lexer_string();
+ }
+ else if (nt_name == PUGIXML_TEXT("processing-instruction"))
+ {
+ if (_lexer.current() != lex_quoted_string)
+ throw_error("Only literals are allowed as arguments to processing-instruction()");
+
+ nt_type = nodetest_pi;
+ nt_name = _lexer.contents();
+ _lexer.next();
+
+ if (_lexer.current() != lex_close_brace)
+ throw_error("Unmatched brace near processing-instruction()");
+ _lexer.next();
+ }
+ else
+ throw_error("Unmatched brace near node type test");
+
+ }
+ // QName or NCName:*
+ else
+ {
+ if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:*
+ {
+ nt_name.end--; // erase *
+
+ nt_type = nodetest_all_in_namespace;
+ }
+ else nt_type = nodetest_name;
+ }
+ }
+ }
+ else if (_lexer.current() == lex_multiply)
+ {
+ nt_type = nodetest_all;
+ _lexer.next();
+ }
+ else throw_error("Unrecognized node test");
+
+ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name));
+
+ xpath_ast_node* last = 0;
+
+ while (_lexer.current() == lex_open_square_brace)
+ {
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_expression();
+
+ xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr);
+
+ if (_lexer.current() != lex_close_square_brace)
+ throw_error("Unmatched square brace");
+ _lexer.next();
+
+ if (last) last->set_next(pred);
+ else n->set_right(pred);
+
+ last = pred;
+ }
+
+ return n;
+ }
+
+ // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+ xpath_ast_node* parse_relative_location_path(xpath_ast_node* set)
+ {
+ xpath_ast_node* n = parse_step(set);
+
+ while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
+ {
+ lexeme_t l = _lexer.current();
+ _lexer.next();
+
+ if (l == lex_double_slash)
+ n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+
+ n = parse_step(n);
+ }
+
+ return n;
+ }
+
+ // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+ // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+ xpath_ast_node* parse_location_path()
+ {
+ if (_lexer.current() == lex_slash)
+ {
+ _lexer.next();
+
+ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set);
+
+ // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path
+ lexeme_t l = _lexer.current();
+
+ if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply)
+ return parse_relative_location_path(n);
+ else
+ return n;
+ }
+ else if (_lexer.current() == lex_double_slash)
+ {
+ _lexer.next();
+
+ xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set);
+ n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+
+ return parse_relative_location_path(n);
+ }
+
+ // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1
+ return parse_relative_location_path(0);
+ }
+
+ // PathExpr ::= LocationPath
+ // | FilterExpr
+ // | FilterExpr '/' RelativeLocationPath
+ // | FilterExpr '//' RelativeLocationPath
+ xpath_ast_node* parse_path_expression()
+ {
+ // Clarification.
+ // PathExpr begins with either LocationPath or FilterExpr.
+ // FilterExpr begins with PrimaryExpr
+ // PrimaryExpr begins with '$' in case of it being a variable reference,
+ // '(' in case of it being an expression, string literal, number constant or
+ // function call.
+
+ if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace ||
+ _lexer.current() == lex_quoted_string || _lexer.current() == lex_number ||
+ _lexer.current() == lex_string)
+ {
+ if (_lexer.current() == lex_string)
+ {
+ // This is either a function call, or not - if not, we shall proceed with location path
+ const char_t* state = _lexer.state();
+
+ while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state;
+
+ if (*state != '(') return parse_location_path();
+
+ // This looks like a function call; however this still can be a node-test. Check it.
+ if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path();
+ }
+
+ xpath_ast_node* n = parse_filter_expression();
+
+ if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
+ {
+ lexeme_t l = _lexer.current();
+ _lexer.next();
+
+ if (l == lex_double_slash)
+ {
+ if (n->rettype() != xpath_type_node_set) throw_error("Step has to be applied to node set");
+
+ n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+ }
+
+ // select from location path
+ return parse_relative_location_path(n);
+ }
+
+ return n;
+ }
+ else return parse_location_path();
+ }
+
+ // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+ xpath_ast_node* parse_union_expression()
+ {
+ xpath_ast_node* n = parse_path_expression();
+
+ while (_lexer.current() == lex_union)
+ {
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_union_expression();
+
+ if (n->rettype() != xpath_type_node_set || expr->rettype() != xpath_type_node_set)
+ throw_error("Union operator has to be applied to node sets");
+
+ n = new (alloc_node()) xpath_ast_node(ast_op_union, xpath_type_node_set, n, expr);
+ }
+
+ return n;
+ }
+
+ // UnaryExpr ::= UnionExpr | '-' UnaryExpr
+ xpath_ast_node* parse_unary_expression()
+ {
+ if (_lexer.current() == lex_minus)
+ {
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_unary_expression();
+
+ return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr);
+ }
+ else return parse_union_expression();
+ }
+
+ // MultiplicativeExpr ::= UnaryExpr
+ // | MultiplicativeExpr '*' UnaryExpr
+ // | MultiplicativeExpr 'div' UnaryExpr
+ // | MultiplicativeExpr 'mod' UnaryExpr
+ xpath_ast_node* parse_multiplicative_expression()
+ {
+ xpath_ast_node* n = parse_unary_expression();
+
+ while (_lexer.current() == lex_multiply || (_lexer.current() == lex_string &&
+ (_lexer.contents() == PUGIXML_TEXT("mod") || _lexer.contents() == PUGIXML_TEXT("div"))))
+ {
+ ast_type_t op = _lexer.current() == lex_multiply ? ast_op_multiply :
+ _lexer.contents().begin[0] == 'd' ? ast_op_divide : ast_op_mod;
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_unary_expression();
+
+ n = new (alloc_node()) xpath_ast_node(op, xpath_type_number, n, expr);
+ }
+
+ return n;
+ }
+
+ // AdditiveExpr ::= MultiplicativeExpr
+ // | AdditiveExpr '+' MultiplicativeExpr
+ // | AdditiveExpr '-' MultiplicativeExpr
+ xpath_ast_node* parse_additive_expression()
+ {
+ xpath_ast_node* n = parse_multiplicative_expression();
+
+ while (_lexer.current() == lex_plus || _lexer.current() == lex_minus)
+ {
+ lexeme_t l = _lexer.current();
+
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_multiplicative_expression();
+
+ n = new (alloc_node()) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, xpath_type_number, n, expr);
+ }
+
+ return n;
+ }
+
+ // RelationalExpr ::= AdditiveExpr
+ // | RelationalExpr '<' AdditiveExpr
+ // | RelationalExpr '>' AdditiveExpr
+ // | RelationalExpr '<=' AdditiveExpr
+ // | RelationalExpr '>=' AdditiveExpr
+ xpath_ast_node* parse_relational_expression()
+ {
+ xpath_ast_node* n = parse_additive_expression();
+
+ while (_lexer.current() == lex_less || _lexer.current() == lex_less_or_equal ||
+ _lexer.current() == lex_greater || _lexer.current() == lex_greater_or_equal)
+ {
+ lexeme_t l = _lexer.current();
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_additive_expression();
+
+ n = new (alloc_node()) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater :
+ l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal, xpath_type_boolean, n, expr);
+ }
+
+ return n;
+ }
+
+ // EqualityExpr ::= RelationalExpr
+ // | EqualityExpr '=' RelationalExpr
+ // | EqualityExpr '!=' RelationalExpr
+ xpath_ast_node* parse_equality_expression()
+ {
+ xpath_ast_node* n = parse_relational_expression();
+
+ while (_lexer.current() == lex_equal || _lexer.current() == lex_not_equal)
+ {
+ lexeme_t l = _lexer.current();
+
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_relational_expression();
+
+ n = new (alloc_node()) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, xpath_type_boolean, n, expr);
+ }
+
+ return n;
+ }
+
+ // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+ xpath_ast_node* parse_and_expression()
+ {
+ xpath_ast_node* n = parse_equality_expression();
+
+ while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("and"))
+ {
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_equality_expression();
+
+ n = new (alloc_node()) xpath_ast_node(ast_op_and, xpath_type_boolean, n, expr);
+ }
+
+ return n;
+ }
+
+ // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+ xpath_ast_node* parse_or_expression()
+ {
+ xpath_ast_node* n = parse_and_expression();
+
+ while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("or"))
+ {
+ _lexer.next();
+
+ xpath_ast_node* expr = parse_and_expression();
+
+ n = new (alloc_node()) xpath_ast_node(ast_op_or, xpath_type_boolean, n, expr);
+ }
+
+ return n;
+ }
+
+ // Expr ::= OrExpr
+ xpath_ast_node* parse_expression()
+ {
+ return parse_or_expression();
+ }
+
+ xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)
+ {
+ }
+
+ xpath_ast_node* parse()
+ {
+ xpath_ast_node* result = parse_expression();
+
+ if (_lexer.current() != lex_eof)
+ {
+ // there are still unparsed tokens left, error
+ throw_error("Incorrect query");
+ }
+
+ return result;
+ }
+
+ static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)
+ {
+ xpath_parser parser(query, variables, alloc, result);
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ int error = setjmp(parser._error_handler);
+
+ return (error == 0) ? parser.parse() : 0;
+ #else
+ return parser.parse();
+ #endif
+ }
+ };
+
+ struct xpath_query_impl
+ {
+ static xpath_query_impl* create()
+ {
+ void* memory = xml_memory::allocate(sizeof(xpath_query_impl));
+
+ return new (memory) xpath_query_impl();
+ }
+
+ static void destroy(void* ptr)
+ {
+ if (!ptr) return;
+
+ // free all allocated pages
+ static_cast<xpath_query_impl*>(ptr)->alloc.release();
+
+ // free allocator memory (with the first page)
+ xml_memory::deallocate(ptr);
+ }
+
+ xpath_query_impl(): root(0), alloc(&block)
+ {
+ block.next = 0;
+ }
+
+ xpath_ast_node* root;
+ xpath_allocator alloc;
+ xpath_memory_block block;
+ };
+
+ PUGI__FN xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd)
+ {
+ if (!impl) return xpath_string();
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ if (setjmp(sd.error_handler)) return xpath_string();
+ #endif
+
+ xpath_context c(n, 1, 1);
+
+ return impl->root->eval_string(c, sd.stack);
+ }
+PUGI__NS_END
+
+namespace pugi
+{
+#ifndef PUGIXML_NO_EXCEPTIONS
+ PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_)
+ {
+ assert(_result.error);
+ }
+
+ PUGI__FN const char* xpath_exception::what() const throw()
+ {
+ return _result.error;
+ }
+
+ PUGI__FN const xpath_parse_result& xpath_exception::result() const
+ {
+ return _result;
+ }
+#endif
+
+ PUGI__FN xpath_node::xpath_node()
+ {
+ }
+
+ PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_)
+ {
+ }
+
+ PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)
+ {
+ }
+
+ PUGI__FN xml_node xpath_node::node() const
+ {
+ return _attribute ? xml_node() : _node;
+ }
+
+ PUGI__FN xml_attribute xpath_node::attribute() const
+ {
+ return _attribute;
+ }
+
+ PUGI__FN xml_node xpath_node::parent() const
+ {
+ return _attribute ? _node : _node.parent();
+ }
+
+ PUGI__FN static void unspecified_bool_xpath_node(xpath_node***)
+ {
+ }
+
+ PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const
+ {
+ return (_node || _attribute) ? unspecified_bool_xpath_node : 0;
+ }
+
+ PUGI__FN bool xpath_node::operator!() const
+ {
+ return !(_node || _attribute);
+ }
+
+ PUGI__FN bool xpath_node::operator==(const xpath_node& n) const
+ {
+ return _node == n._node && _attribute == n._attribute;
+ }
+
+ PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const
+ {
+ return _node != n._node || _attribute != n._attribute;
+ }
+
+#ifdef __BORLANDC__
+ PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs)
+ {
+ return (bool)lhs && rhs;
+ }
+
+ PUGI__FN bool operator||(const xpath_node& lhs, bool rhs)
+ {
+ return (bool)lhs || rhs;
+ }
+#endif
+
+ PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_)
+ {
+ assert(begin_ <= end_);
+
+ size_t size_ = static_cast<size_t>(end_ - begin_);
+
+ if (size_ <= 1)
+ {
+ // deallocate old buffer
+ if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
+
+ // use internal buffer
+ if (begin_ != end_) _storage = *begin_;
+
+ _begin = &_storage;
+ _end = &_storage + size_;
+ }
+ else
+ {
+ // make heap copy
+ xpath_node* storage = static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
+
+ if (!storage)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return;
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+
+ memcpy(storage, begin_, size_ * sizeof(xpath_node));
+
+ // deallocate old buffer
+ if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
+
+ // finalize
+ _begin = storage;
+ _end = storage + size_;
+ }
+ }
+
+ PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+ {
+ }
+
+ PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_), _begin(&_storage), _end(&_storage)
+ {
+ _assign(begin_, end_);
+ }
+
+ PUGI__FN xpath_node_set::~xpath_node_set()
+ {
+ if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
+ }
+
+ PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage)
+ {
+ _assign(ns._begin, ns._end);
+ }
+
+ PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)
+ {
+ if (this == &ns) return *this;
+
+ _type = ns._type;
+ _assign(ns._begin, ns._end);
+
+ return *this;
+ }
+
+ PUGI__FN xpath_node_set::type_t xpath_node_set::type() const
+ {
+ return _type;
+ }
+
+ PUGI__FN size_t xpath_node_set::size() const
+ {
+ return _end - _begin;
+ }
+
+ PUGI__FN bool xpath_node_set::empty() const
+ {
+ return _begin == _end;
+ }
+
+ PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const
+ {
+ assert(index < size());
+ return _begin[index];
+ }
+
+ PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const
+ {
+ return _begin;
+ }
+
+ PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const
+ {
+ return _end;
+ }
+
+ PUGI__FN void xpath_node_set::sort(bool reverse)
+ {
+ _type = impl::xpath_sort(_begin, _end, _type, reverse);
+ }
+
+ PUGI__FN xpath_node xpath_node_set::first() const
+ {
+ return impl::xpath_first(_begin, _end, _type);
+ }
+
+ PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0)
+ {
+ }
+
+ PUGI__FN xpath_parse_result::operator bool() const
+ {
+ return error == 0;
+ }
+
+ PUGI__FN const char* xpath_parse_result::description() const
+ {
+ return error ? error : "No error";
+ }
+
+ PUGI__FN xpath_variable::xpath_variable()
+ {
+ }
+
+ PUGI__FN const char_t* xpath_variable::name() const
+ {
+ switch (_type)
+ {
+ case xpath_type_node_set:
+ return static_cast<const impl::xpath_variable_node_set*>(this)->name;
+
+ case xpath_type_number:
+ return static_cast<const impl::xpath_variable_number*>(this)->name;
+
+ case xpath_type_string:
+ return static_cast<const impl::xpath_variable_string*>(this)->name;
+
+ case xpath_type_boolean:
+ return static_cast<const impl::xpath_variable_boolean*>(this)->name;
+
+ default:
+ assert(!"Invalid variable type");
+ return 0;
+ }
+ }
+
+ PUGI__FN xpath_value_type xpath_variable::type() const
+ {
+ return _type;
+ }
+
+ PUGI__FN bool xpath_variable::get_boolean() const
+ {
+ return (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false;
+ }
+
+ PUGI__FN double xpath_variable::get_number() const
+ {
+ return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan();
+ }
+
+ PUGI__FN const char_t* xpath_variable::get_string() const
+ {
+ const char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : 0;
+ return value ? value : PUGIXML_TEXT("");
+ }
+
+ PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const
+ {
+ return (_type == xpath_type_node_set) ? static_cast<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;
+ }
+
+ PUGI__FN bool xpath_variable::set(bool value)
+ {
+ if (_type != xpath_type_boolean) return false;
+
+ static_cast<impl::xpath_variable_boolean*>(this)->value = value;
+ return true;
+ }
+
+ PUGI__FN bool xpath_variable::set(double value)
+ {
+ if (_type != xpath_type_number) return false;
+
+ static_cast<impl::xpath_variable_number*>(this)->value = value;
+ return true;
+ }
+
+ PUGI__FN bool xpath_variable::set(const char_t* value)
+ {
+ if (_type != xpath_type_string) return false;
+
+ impl::xpath_variable_string* var = static_cast<impl::xpath_variable_string*>(this);
+
+ // duplicate string
+ size_t size = (impl::strlength(value) + 1) * sizeof(char_t);
+
+ char_t* copy = static_cast<char_t*>(impl::xml_memory::allocate(size));
+ if (!copy) return false;
+
+ memcpy(copy, value, size);
+
+ // replace old string
+ if (var->value) impl::xml_memory::deallocate(var->value);
+ var->value = copy;
+
+ return true;
+ }
+
+ PUGI__FN bool xpath_variable::set(const xpath_node_set& value)
+ {
+ if (_type != xpath_type_node_set) return false;
+
+ static_cast<impl::xpath_variable_node_set*>(this)->value = value;
+ return true;
+ }
+
+ PUGI__FN xpath_variable_set::xpath_variable_set()
+ {
+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0;
+ }
+
+ PUGI__FN xpath_variable_set::~xpath_variable_set()
+ {
+ for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+ {
+ xpath_variable* var = _data[i];
+
+ while (var)
+ {
+ xpath_variable* next = var->_next;
+
+ impl::delete_xpath_variable(var->_type, var);
+
+ var = next;
+ }
+ }
+ }
+
+ PUGI__FN xpath_variable* xpath_variable_set::find(const char_t* name) const
+ {
+ const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
+ size_t hash = impl::hash_string(name) % hash_size;
+
+ // look for existing variable
+ for (xpath_variable* var = _data[hash]; var; var = var->_next)
+ if (impl::strequal(var->name(), name))
+ return var;
+
+ return 0;
+ }
+
+ PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
+ {
+ const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
+ size_t hash = impl::hash_string(name) % hash_size;
+
+ // look for existing variable
+ for (xpath_variable* var = _data[hash]; var; var = var->_next)
+ if (impl::strequal(var->name(), name))
+ return var->type() == type ? var : 0;
+
+ // add new variable
+ xpath_variable* result = impl::new_xpath_variable(type, name);
+
+ if (result)
+ {
+ result->_type = type;
+ result->_next = _data[hash];
+
+ _data[hash] = result;
+ }
+
+ return result;
+ }
+
+ PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value)
+ {
+ xpath_variable* var = add(name, xpath_type_boolean);
+ return var ? var->set(value) : false;
+ }
+
+ PUGI__FN bool xpath_variable_set::set(const char_t* name, double value)
+ {
+ xpath_variable* var = add(name, xpath_type_number);
+ return var ? var->set(value) : false;
+ }
+
+ PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value)
+ {
+ xpath_variable* var = add(name, xpath_type_string);
+ return var ? var->set(value) : false;
+ }
+
+ PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)
+ {
+ xpath_variable* var = add(name, xpath_type_node_set);
+ return var ? var->set(value) : false;
+ }
+
+ PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)
+ {
+ return find(name);
+ }
+
+ PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
+ {
+ return find(name);
+ }
+
+ PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)
+ {
+ impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create();
+
+ if (!qimpl)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ _result.error = "Out of memory";
+ #else
+ throw std::bad_alloc();
+ #endif
+ }
+ else
+ {
+ impl::buffer_holder impl_holder(qimpl, impl::xpath_query_impl::destroy);
+
+ qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
+
+ if (qimpl->root)
+ {
+ _impl = static_cast<impl::xpath_query_impl*>(impl_holder.release());
+ _result.error = 0;
+ }
+ }
+ }
+
+ PUGI__FN xpath_query::~xpath_query()
+ {
+ impl::xpath_query_impl::destroy(_impl);
+ }
+
+ PUGI__FN xpath_value_type xpath_query::return_type() const
+ {
+ if (!_impl) return xpath_type_none;
+
+ return static_cast<impl::xpath_query_impl*>(_impl)->root->rettype();
+ }
+
+ PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const
+ {
+ if (!_impl) return false;
+
+ impl::xpath_context c(n, 1, 1);
+ impl::xpath_stack_data sd;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ if (setjmp(sd.error_handler)) return false;
+ #endif
+
+ return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);
+ }
+
+ PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const
+ {
+ if (!_impl) return impl::gen_nan();
+
+ impl::xpath_context c(n, 1, 1);
+ impl::xpath_stack_data sd;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ if (setjmp(sd.error_handler)) return impl::gen_nan();
+ #endif
+
+ return static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);
+ }
+
+#ifndef PUGIXML_NO_STL
+ PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const
+ {
+ impl::xpath_stack_data sd;
+
+ return impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd).c_str();
+ }
+#endif
+
+ PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const
+ {
+ impl::xpath_stack_data sd;
+
+ impl::xpath_string r = impl::evaluate_string_impl(static_cast<impl::xpath_query_impl*>(_impl), n, sd);
+
+ size_t full_size = r.length() + 1;
+
+ if (capacity > 0)
+ {
+ size_t size = (full_size < capacity) ? full_size : capacity;
+ assert(size > 0);
+
+ memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t));
+ buffer[size - 1] = 0;
+ }
+
+ return full_size;
+ }
+
+ PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const
+ {
+ if (!_impl) return xpath_node_set();
+
+ impl::xpath_ast_node* root = static_cast<impl::xpath_query_impl*>(_impl)->root;
+
+ if (root->rettype() != xpath_type_node_set)
+ {
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ return xpath_node_set();
+ #else
+ xpath_parse_result res;
+ res.error = "Expression does not evaluate to node set";
+
+ throw xpath_exception(res);
+ #endif
+ }
+
+ impl::xpath_context c(n, 1, 1);
+ impl::xpath_stack_data sd;
+
+ #ifdef PUGIXML_NO_EXCEPTIONS
+ if (setjmp(sd.error_handler)) return xpath_node_set();
+ #endif
+
+ impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack);
+
+ return xpath_node_set(r.begin(), r.end(), r.type());
+ }
+
+ PUGI__FN const xpath_parse_result& xpath_query::result() const
+ {
+ return _result;
+ }
+
+ PUGI__FN static void unspecified_bool_xpath_query(xpath_query***)
+ {
+ }
+
+ PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const
+ {
+ return _impl ? unspecified_bool_xpath_query : 0;
+ }
+
+ PUGI__FN bool xpath_query::operator!() const
+ {
+ return !_impl;
+ }
+
+ PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
+ {
+ xpath_query q(query, variables);
+ return select_single_node(q);
+ }
+
+ PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
+ {
+ xpath_node_set s = query.evaluate_node_set(*this);
+ return s.empty() ? xpath_node() : s.first();
+ }
+
+ PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
+ {
+ xpath_query q(query, variables);
+ return select_nodes(q);
+ }
+
+ PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
+ {
+ return query.evaluate_node_set(*this);
+ }
+}
+
+#endif
+
+#ifdef __BORLANDC__
+# pragma option pop
+#endif
+
+// Intel C++ does not properly keep warning state for function templates,
+// so popping warning state at the end of translation unit leads to warnings in the middle.
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
+# pragma warning(pop)
+#endif
+
+// Undefine all local macros (makes sure we're not leaking macros in header-only mode)
+#undef PUGI__NO_INLINE
+#undef PUGI__STATIC_ASSERT
+#undef PUGI__DMC_VOLATILE
+#undef PUGI__MSVC_CRT_VERSION
+#undef PUGI__NS_BEGIN
+#undef PUGI__NS_END
+#undef PUGI__FN
+#undef PUGI__FN_NO_INLINE
+#undef PUGI__IS_CHARTYPE_IMPL
+#undef PUGI__IS_CHARTYPE
+#undef PUGI__IS_CHARTYPEX
+#undef PUGI__SKIPWS
+#undef PUGI__OPTSET
+#undef PUGI__PUSHNODE
+#undef PUGI__POPNODE
+#undef PUGI__SCANFOR
+#undef PUGI__SCANWHILE
+#undef PUGI__ENDSEG
+#undef PUGI__THROW_ERROR
+#undef PUGI__CHECK_ERROR
+
+#endif
+
+/**
+ * Copyright (c) 2006-2012 Arseny Kapoulkine
+ *
+ * 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.
+ */
diff --git a/src/pugixml/pugixml.hpp b/src/pugixml/pugixml.hpp
new file mode 100644
index 0000000..77b4dcf
--- /dev/null
+++ b/src/pugixml/pugixml.hpp
@@ -0,0 +1,1265 @@
+/**
+ * pugixml parser - version 1.2
+ * --------------------------------------------------------
+ * Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at http://pugixml.org/
+ *
+ * This library is distributed under the MIT License. See notice at the end
+ * of this file.
+ *
+ * This work is based on the pugxml parser, which is:
+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+ */
+
+#ifndef PUGIXML_VERSION
+// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
+# define PUGIXML_VERSION 120
+#endif
+
+// Include user configuration file (this can define various configuration macros)
+#include "pugiconfig.hpp"
+
+#ifndef HEADER_PUGIXML_HPP
+#define HEADER_PUGIXML_HPP
+
+// Include stddef.h for size_t and ptrdiff_t
+#include <stddef.h>
+
+// Include exception header for XPath
+#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)
+# include <exception>
+#endif
+
+// Include STL headers
+#ifndef PUGIXML_NO_STL
+# include <iterator>
+# include <iosfwd>
+# include <string>
+#endif
+
+// Macro for deprecated features
+#ifndef PUGIXML_DEPRECATED
+# if defined(__GNUC__)
+# define PUGIXML_DEPRECATED __attribute__((deprecated))
+# elif defined(_MSC_VER) && _MSC_VER >= 1300
+# define PUGIXML_DEPRECATED __declspec(deprecated)
+# else
+# define PUGIXML_DEPRECATED
+# endif
+#endif
+
+// If no API is defined, assume default
+#ifndef PUGIXML_API
+# define PUGIXML_API
+#endif
+
+// If no API for classes is defined, assume default
+#ifndef PUGIXML_CLASS
+# define PUGIXML_CLASS PUGIXML_API
+#endif
+
+// If no API for functions is defined, assume default
+#ifndef PUGIXML_FUNCTION
+# define PUGIXML_FUNCTION PUGIXML_API
+#endif
+
+// Character interface macros
+#ifdef PUGIXML_WCHAR_MODE
+# define PUGIXML_TEXT(t) L ## t
+# define PUGIXML_CHAR wchar_t
+#else
+# define PUGIXML_TEXT(t) t
+# define PUGIXML_CHAR char
+#endif
+
+namespace pugi
+{
+ // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE
+ typedef PUGIXML_CHAR char_t;
+
+#ifndef PUGIXML_NO_STL
+ // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
+ typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;
+#endif
+}
+
+// The PugiXML namespace
+namespace pugi
+{
+ // Tree node types
+ enum xml_node_type
+ {
+ node_null, // Empty (null) node handle
+ node_document, // A document tree's absolute root
+ node_element, // Element tag, i.e. '<node/>'
+ node_pcdata, // Plain character data, i.e. 'text'
+ node_cdata, // Character data, i.e. '<![CDATA[text]]>'
+ node_comment, // Comment tag, i.e. '<!-- text -->'
+ node_pi, // Processing instruction, i.e. '<?name?>'
+ node_declaration, // Document declaration, i.e. '<?xml version="1.0"?>'
+ node_doctype // Document type declaration, i.e. '<!DOCTYPE doc>'
+ };
+
+ // Parsing options
+
+ // Minimal parsing mode (equivalent to turning all other flags off).
+ // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.
+ const unsigned int parse_minimal = 0x0000;
+
+ // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.
+ const unsigned int parse_pi = 0x0001;
+
+ // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.
+ const unsigned int parse_comments = 0x0002;
+
+ // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.
+ const unsigned int parse_cdata = 0x0004;
+
+ // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.
+ // This flag is off by default; turning it on usually results in slower parsing and more memory consumption.
+ const unsigned int parse_ws_pcdata = 0x0008;
+
+ // This flag determines if character and entity references are expanded during parsing. This flag is on by default.
+ const unsigned int parse_escapes = 0x0010;
+
+ // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.
+ const unsigned int parse_eol = 0x0020;
+
+ // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.
+ const unsigned int parse_wconv_attribute = 0x0040;
+
+ // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.
+ const unsigned int parse_wnorm_attribute = 0x0080;
+
+ // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.
+ const unsigned int parse_declaration = 0x0100;
+
+ // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.
+ const unsigned int parse_doctype = 0x0200;
+
+ // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only
+ // of whitespace is added to the DOM tree.
+ // This flag is off by default; turning it on may result in slower parsing and more memory consumption.
+ const unsigned int parse_ws_pcdata_single = 0x0400;
+
+ // The default parsing mode.
+ // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
+ // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
+ const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;
+
+ // The full parsing mode.
+ // Nodes of all types are added to the DOM tree, character/reference entities are expanded,
+ // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
+ const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;
+
+ // These flags determine the encoding of input data for XML document
+ enum xml_encoding
+ {
+ encoding_auto, // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found
+ encoding_utf8, // UTF8 encoding
+ encoding_utf16_le, // Little-endian UTF16
+ encoding_utf16_be, // Big-endian UTF16
+ encoding_utf16, // UTF16 with native endianness
+ encoding_utf32_le, // Little-endian UTF32
+ encoding_utf32_be, // Big-endian UTF32
+ encoding_utf32, // UTF32 with native endianness
+ encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32)
+ encoding_latin1
+ };
+
+ // Formatting flags
+
+ // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
+ const unsigned int format_indent = 0x01;
+
+ // Write encoding-specific BOM to the output stream. This flag is off by default.
+ const unsigned int format_write_bom = 0x02;
+
+ // Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
+ const unsigned int format_raw = 0x04;
+
+ // Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
+ const unsigned int format_no_declaration = 0x08;
+
+ // Don't escape attribute values and PCDATA contents. This flag is off by default.
+ const unsigned int format_no_escapes = 0x10;
+
+ // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
+ const unsigned int format_save_file_text = 0x20;
+
+ // The default set of formatting flags.
+ // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
+ const unsigned int format_default = format_indent;
+
+ // Forward declarations
+ struct xml_attribute_struct;
+ struct xml_node_struct;
+
+ class xml_node_iterator;
+ class xml_attribute_iterator;
+ class xml_named_node_iterator;
+
+ class xml_tree_walker;
+
+ class xml_node;
+
+ class xml_text;
+
+ #ifndef PUGIXML_NO_XPATH
+ class xpath_node;
+ class xpath_node_set;
+ class xpath_query;
+ class xpath_variable_set;
+ #endif
+
+ // Range-based for loop support
+ template <typename It> class xml_object_range
+ {
+ public:
+ typedef It const_iterator;
+
+ xml_object_range(It b, It e): _begin(b), _end(e)
+ {
+ }
+
+ It begin() const { return _begin; }
+ It end() const { return _end; }
+
+ private:
+ It _begin, _end;
+ };
+
+ // Writer interface for node printing (see xml_node::print)
+ class PUGIXML_CLASS xml_writer
+ {
+ public:
+ virtual ~xml_writer() {}
+
+ // Write memory chunk into stream/file/whatever
+ virtual void write(const void* data, size_t size) = 0;
+ };
+
+ // xml_writer implementation for FILE*
+ class PUGIXML_CLASS xml_writer_file: public xml_writer
+ {
+ public:
+ // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio
+ xml_writer_file(void* file);
+
+ virtual void write(const void* data, size_t size);
+
+ private:
+ void* file;
+ };
+
+ #ifndef PUGIXML_NO_STL
+ // xml_writer implementation for streams
+ class PUGIXML_CLASS xml_writer_stream: public xml_writer
+ {
+ public:
+ // Construct writer from an output stream object
+ xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);
+ xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);
+
+ virtual void write(const void* data, size_t size);
+
+ private:
+ std::basic_ostream<char, std::char_traits<char> >* narrow_stream;
+ std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;
+ };
+ #endif
+
+ // A light-weight handle for manipulating attributes in DOM tree
+ class PUGIXML_CLASS xml_attribute
+ {
+ friend class xml_attribute_iterator;
+ friend class xml_node;
+
+ private:
+ xml_attribute_struct* _attr;
+
+ typedef void (*unspecified_bool_type)(xml_attribute***);
+
+ public:
+ // Default constructor. Constructs an empty attribute.
+ xml_attribute();
+
+ // Constructs attribute from internal pointer
+ explicit xml_attribute(xml_attribute_struct* attr);
+
+ // Safe bool conversion operator
+ operator unspecified_bool_type() const;
+
+ // Borland C++ workaround
+ bool operator!() const;
+
+ // Comparison operators (compares wrapped attribute pointers)
+ bool operator==(const xml_attribute& r) const;
+ bool operator!=(const xml_attribute& r) const;
+ bool operator<(const xml_attribute& r) const;
+ bool operator>(const xml_attribute& r) const;
+ bool operator<=(const xml_attribute& r) const;
+ bool operator>=(const xml_attribute& r) const;
+
+ // Check if attribute is empty
+ bool empty() const;
+
+ // Get attribute name/value, or "" if attribute is empty
+ const char_t* name() const;
+ const char_t* value() const;
+
+ // Get attribute value, or the default value if attribute is empty
+ const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
+
+ // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty
+ int as_int(int def = 0) const;
+ unsigned int as_uint(unsigned int def = 0) const;
+ double as_double(double def = 0) const;
+ float as_float(float def = 0) const;
+
+ // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
+ bool as_bool(bool def = false) const;
+
+ // Set attribute name/value (returns false if attribute is empty or there is not enough memory)
+ bool set_name(const char_t* rhs);
+ bool set_value(const char_t* rhs);
+
+ // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
+ bool set_value(int rhs);
+ bool set_value(unsigned int rhs);
+ bool set_value(double rhs);
+ bool set_value(bool rhs);
+
+ // Set attribute value (equivalent to set_value without error checking)
+ xml_attribute& operator=(const char_t* rhs);
+ xml_attribute& operator=(int rhs);
+ xml_attribute& operator=(unsigned int rhs);
+ xml_attribute& operator=(double rhs);
+ xml_attribute& operator=(bool rhs);
+
+ // Get next/previous attribute in the attribute list of the parent node
+ xml_attribute next_attribute() const;
+ xml_attribute previous_attribute() const;
+
+ // Get hash value (unique for handles to the same object)
+ size_t hash_value() const;
+
+ // Get internal pointer
+ xml_attribute_struct* internal_object() const;
+ };
+
+#ifdef __BORLANDC__
+ // Borland C++ workaround
+ bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);
+ bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);
+#endif
+
+ // A light-weight handle for manipulating nodes in DOM tree
+ class PUGIXML_CLASS xml_node
+ {
+ friend class xml_attribute_iterator;
+ friend class xml_node_iterator;
+ friend class xml_named_node_iterator;
+
+ protected:
+ xml_node_struct* _root;
+
+ typedef void (*unspecified_bool_type)(xml_node***);
+
+ public:
+ // Default constructor. Constructs an empty node.
+ xml_node();
+
+ // Constructs node from internal pointer
+ explicit xml_node(xml_node_struct* p);
+
+ // Safe bool conversion operator
+ operator unspecified_bool_type() const;
+
+ // Borland C++ workaround
+ bool operator!() const;
+
+ // Comparison operators (compares wrapped node pointers)
+ bool operator==(const xml_node& r) const;
+ bool operator!=(const xml_node& r) const;
+ bool operator<(const xml_node& r) const;
+ bool operator>(const xml_node& r) const;
+ bool operator<=(const xml_node& r) const;
+ bool operator>=(const xml_node& r) const;
+
+ // Check if node is empty.
+ bool empty() const;
+
+ // Get node type
+ xml_node_type type() const;
+
+ // Get node name/value, or "" if node is empty or it has no name/value
+ const char_t* name() const;
+ const char_t* value() const;
+
+ // Get attribute list
+ xml_attribute first_attribute() const;
+ xml_attribute last_attribute() const;
+
+ // Get children list
+ xml_node first_child() const;
+ xml_node last_child() const;
+
+ // Get next/previous sibling in the children list of the parent node
+ xml_node next_sibling() const;
+ xml_node previous_sibling() const;
+
+ // Get parent node
+ xml_node parent() const;
+
+ // Get root of DOM tree this node belongs to
+ xml_node root() const;
+
+ // Get text object for the current node
+ xml_text text() const;
+
+ // Get child, attribute or next/previous sibling with the specified name
+ xml_node child(const char_t* name) const;
+ xml_attribute attribute(const char_t* name) const;
+ xml_node next_sibling(const char_t* name) const;
+ xml_node previous_sibling(const char_t* name) const;
+
+ // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
+ const char_t* child_value() const;
+
+ // Get child value of child with specified name. Equivalent to child(name).child_value().
+ const char_t* child_value(const char_t* name) const;
+
+ // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
+ bool set_name(const char_t* rhs);
+ bool set_value(const char_t* rhs);
+
+ // Add attribute with specified name. Returns added attribute, or empty attribute on errors.
+ xml_attribute append_attribute(const char_t* name);
+ xml_attribute prepend_attribute(const char_t* name);
+ xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);
+ xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);
+
+ // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.
+ xml_attribute append_copy(const xml_attribute& proto);
+ xml_attribute prepend_copy(const xml_attribute& proto);
+ xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
+ xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
+
+ // Add child node with specified type. Returns added node, or empty node on errors.
+ xml_node append_child(xml_node_type type = node_element);
+ xml_node prepend_child(xml_node_type type = node_element);
+ xml_node insert_child_after(xml_node_type type, const xml_node& node);
+ xml_node insert_child_before(xml_node_type type, const xml_node& node);
+
+ // Add child element with specified name. Returns added node, or empty node on errors.
+ xml_node append_child(const char_t* name);
+ xml_node prepend_child(const char_t* name);
+ xml_node insert_child_after(const char_t* name, const xml_node& node);
+ xml_node insert_child_before(const char_t* name, const xml_node& node);
+
+ // Add a copy of the specified node as a child. Returns added node, or empty node on errors.
+ xml_node append_copy(const xml_node& proto);
+ xml_node prepend_copy(const xml_node& proto);
+ xml_node insert_copy_after(const xml_node& proto, const xml_node& node);
+ xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
+
+ // Remove specified attribute
+ bool remove_attribute(const xml_attribute& a);
+ bool remove_attribute(const char_t* name);
+
+ // Remove specified child
+ bool remove_child(const xml_node& n);
+ bool remove_child(const char_t* name);
+
+ // Find attribute using predicate. Returns first attribute for which predicate returned true.
+ template <typename Predicate> xml_attribute find_attribute(Predicate pred) const
+ {
+ if (!_root) return xml_attribute();
+
+ for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
+ if (pred(attrib))
+ return attrib;
+
+ return xml_attribute();
+ }
+
+ // Find child node using predicate. Returns first child for which predicate returned true.
+ template <typename Predicate> xml_node find_child(Predicate pred) const
+ {
+ if (!_root) return xml_node();
+
+ for (xml_node node = first_child(); node; node = node.next_sibling())
+ if (pred(node))
+ return node;
+
+ return xml_node();
+ }
+
+ // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.
+ template <typename Predicate> xml_node find_node(Predicate pred) const
+ {
+ if (!_root) return xml_node();
+
+ xml_node cur = first_child();
+
+ while (cur._root && cur._root != _root)
+ {
+ if (pred(cur)) return cur;
+
+ if (cur.first_child()) cur = cur.first_child();
+ else if (cur.next_sibling()) cur = cur.next_sibling();
+ else
+ {
+ while (!cur.next_sibling() && cur._root != _root) cur = cur.parent();
+
+ if (cur._root != _root) cur = cur.next_sibling();
+ }
+ }
+
+ return xml_node();
+ }
+
+ // Find child node by attribute name/value
+ xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;
+ xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;
+
+ #ifndef PUGIXML_NO_STL
+ // Get the absolute node path from root as a text string.
+ string_t path(char_t delimiter = '/') const;
+ #endif
+
+ // Search for a node by path consisting of node names and . or .. elements.
+ xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;
+
+ // Recursively traverse subtree with xml_tree_walker
+ bool traverse(xml_tree_walker& walker);
+
+ #ifndef PUGIXML_NO_XPATH
+ // Select single node by evaluating XPath query. Returns first node from the resulting node set.
+ xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
+ xpath_node select_single_node(const xpath_query& query) const;
+
+ // Select node set by evaluating XPath query
+ xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
+ xpath_node_set select_nodes(const xpath_query& query) const;
+ #endif
+
+ // Print subtree using a writer object
+ void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
+
+ #ifndef PUGIXML_NO_STL
+ // Print subtree to stream
+ void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
+ void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const;
+ #endif
+
+ // Child nodes iterators
+ typedef xml_node_iterator iterator;
+
+ iterator begin() const;
+ iterator end() const;
+
+ // Attribute iterators
+ typedef xml_attribute_iterator attribute_iterator;
+
+ attribute_iterator attributes_begin() const;
+ attribute_iterator attributes_end() const;
+
+ // Range-based for support
+ xml_object_range<xml_node_iterator> children() const;
+ xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
+ xml_object_range<xml_attribute_iterator> attributes() const;
+
+ // Get node offset in parsed file/string (in char_t units) for debugging purposes
+ ptrdiff_t offset_debug() const;
+
+ // Get hash value (unique for handles to the same object)
+ size_t hash_value() const;
+
+ // Get internal pointer
+ xml_node_struct* internal_object() const;
+ };
+
+#ifdef __BORLANDC__
+ // Borland C++ workaround
+ bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);
+ bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);
+#endif
+
+ // A helper for working with text inside PCDATA nodes
+ class PUGIXML_CLASS xml_text
+ {
+ friend class xml_node;
+
+ xml_node_struct* _root;
+
+ typedef void (*unspecified_bool_type)(xml_text***);
+
+ explicit xml_text(xml_node_struct* root);
+
+ xml_node_struct* _data_new();
+ xml_node_struct* _data() const;
+
+ public:
+ // Default constructor. Constructs an empty object.
+ xml_text();
+
+ // Safe bool conversion operator
+ operator unspecified_bool_type() const;
+
+ // Borland C++ workaround
+ bool operator!() const;
+
+ // Check if text object is empty
+ bool empty() const;
+
+ // Get text, or "" if object is empty
+ const char_t* get() const;
+
+ // Get text, or the default value if object is empty
+ const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
+
+ // Get text as a number, or the default value if conversion did not succeed or object is empty
+ int as_int(int def = 0) const;
+ unsigned int as_uint(unsigned int def = 0) const;
+ double as_double(double def = 0) const;
+ float as_float(float def = 0) const;
+
+ // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
+ bool as_bool(bool def = false) const;
+
+ // Set text (returns false if object is empty or there is not enough memory)
+ bool set(const char_t* rhs);
+
+ // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
+ bool set(int rhs);
+ bool set(unsigned int rhs);
+ bool set(double rhs);
+ bool set(bool rhs);
+
+ // Set text (equivalent to set without error checking)
+ xml_text& operator=(const char_t* rhs);
+ xml_text& operator=(int rhs);
+ xml_text& operator=(unsigned int rhs);
+ xml_text& operator=(double rhs);
+ xml_text& operator=(bool rhs);
+
+ // Get the data node (node_pcdata or node_cdata) for this object
+ xml_node data() const;
+ };
+
+#ifdef __BORLANDC__
+ // Borland C++ workaround
+ bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);
+ bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);
+#endif
+
+ // Child node iterator (a bidirectional iterator over a collection of xml_node)
+ class PUGIXML_CLASS xml_node_iterator
+ {
+ friend class xml_node;
+
+ private:
+ mutable xml_node _wrap;
+ xml_node _parent;
+
+ xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);
+
+ public:
+ // Iterator traits
+ typedef ptrdiff_t difference_type;
+ typedef xml_node value_type;
+ typedef xml_node* pointer;
+ typedef xml_node& reference;
+
+ #ifndef PUGIXML_NO_STL
+ typedef std::bidirectional_iterator_tag iterator_category;
+ #endif
+
+ // Default constructor
+ xml_node_iterator();
+
+ // Construct an iterator which points to the specified node
+ xml_node_iterator(const xml_node& node);
+
+ // Iterator operators
+ bool operator==(const xml_node_iterator& rhs) const;
+ bool operator!=(const xml_node_iterator& rhs) const;
+
+ xml_node& operator*() const;
+ xml_node* operator->() const;
+
+ const xml_node_iterator& operator++();
+ xml_node_iterator operator++(int);
+
+ const xml_node_iterator& operator--();
+ xml_node_iterator operator--(int);
+ };
+
+ // Attribute iterator (a bidirectional iterator over a collection of xml_attribute)
+ class PUGIXML_CLASS xml_attribute_iterator
+ {
+ friend class xml_node;
+
+ private:
+ mutable xml_attribute _wrap;
+ xml_node _parent;
+
+ xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);
+
+ public:
+ // Iterator traits
+ typedef ptrdiff_t difference_type;
+ typedef xml_attribute value_type;
+ typedef xml_attribute* pointer;
+ typedef xml_attribute& reference;
+
+ #ifndef PUGIXML_NO_STL
+ typedef std::bidirectional_iterator_tag iterator_category;
+ #endif
+
+ // Default constructor
+ xml_attribute_iterator();
+
+ // Construct an iterator which points to the specified attribute
+ xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);
+
+ // Iterator operators
+ bool operator==(const xml_attribute_iterator& rhs) const;
+ bool operator!=(const xml_attribute_iterator& rhs) const;
+
+ xml_attribute& operator*() const;
+ xml_attribute* operator->() const;
+
+ const xml_attribute_iterator& operator++();
+ xml_attribute_iterator operator++(int);
+
+ const xml_attribute_iterator& operator--();
+ xml_attribute_iterator operator--(int);
+ };
+
+ // Named node range helper
+ class xml_named_node_iterator
+ {
+ public:
+ // Iterator traits
+ typedef ptrdiff_t difference_type;
+ typedef xml_node value_type;
+ typedef xml_node* pointer;
+ typedef xml_node& reference;
+
+ #ifndef PUGIXML_NO_STL
+ typedef std::forward_iterator_tag iterator_category;
+ #endif
+
+ // Default constructor
+ xml_named_node_iterator();
+
+ // Construct an iterator which points to the specified node
+ xml_named_node_iterator(const xml_node& node, const char_t* name);
+
+ // Iterator operators
+ bool operator==(const xml_named_node_iterator& rhs) const;
+ bool operator!=(const xml_named_node_iterator& rhs) const;
+
+ xml_node& operator*() const;
+ xml_node* operator->() const;
+
+ const xml_named_node_iterator& operator++();
+ xml_named_node_iterator operator++(int);
+
+ private:
+ mutable xml_node _node;
+ const char_t* _name;
+ };
+
+ // Abstract tree walker class (see xml_node::traverse)
+ class PUGIXML_CLASS xml_tree_walker
+ {
+ friend class xml_node;
+
+ private:
+ int _depth;
+
+ protected:
+ // Get current traversal depth
+ int depth() const;
+
+ public:
+ xml_tree_walker();
+ virtual ~xml_tree_walker();
+
+ // Callback that is called when traversal begins
+ virtual bool begin(xml_node& node);
+
+ // Callback that is called for each node traversed
+ virtual bool for_each(xml_node& node) = 0;
+
+ // Callback that is called when traversal ends
+ virtual bool end(xml_node& node);
+ };
+
+ // Parsing status, returned as part of xml_parse_result object
+ enum xml_parse_status
+ {
+ status_ok = 0, // No error
+
+ status_file_not_found, // File was not found during load_file()
+ status_io_error, // Error reading from file/stream
+ status_out_of_memory, // Could not allocate memory
+ status_internal_error, // Internal error occurred
+
+ status_unrecognized_tag, // Parser could not determine tag type
+
+ status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction
+ status_bad_comment, // Parsing error occurred while parsing comment
+ status_bad_cdata, // Parsing error occurred while parsing CDATA section
+ status_bad_doctype, // Parsing error occurred while parsing document type declaration
+ status_bad_pcdata, // Parsing error occurred while parsing PCDATA section
+ status_bad_start_element, // Parsing error occurred while parsing start element tag
+ status_bad_attribute, // Parsing error occurred while parsing element attribute
+ status_bad_end_element, // Parsing error occurred while parsing end element tag
+ status_end_element_mismatch // There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
+ };
+
+ // Parsing result
+ struct PUGIXML_CLASS xml_parse_result
+ {
+ // Parsing status (see xml_parse_status)
+ xml_parse_status status;
+
+ // Last parsed offset (in char_t units from start of input data)
+ ptrdiff_t offset;
+
+ // Source document encoding
+ xml_encoding encoding;
+
+ // Default constructor, initializes object to failed state
+ xml_parse_result();
+
+ // Cast to bool operator
+ operator bool() const;
+
+ // Get error description
+ const char* description() const;
+ };
+
+ // Document class (DOM tree root)
+ class PUGIXML_CLASS xml_document: public xml_node
+ {
+ private:
+ char_t* _buffer;
+
+ char _memory[192];
+
+ // Non-copyable semantics
+ xml_document(const xml_document&);
+ const xml_document& operator=(const xml_document&);
+
+ void create();
+ void destroy();
+
+ xml_parse_result load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own);
+
+ public:
+ // Default constructor, makes empty document
+ xml_document();
+
+ // Destructor, invalidates all node/attribute handles to this document
+ ~xml_document();
+
+ // Removes all nodes, leaving the empty document
+ void reset();
+
+ // Removes all nodes, then copies the entire contents of the specified document
+ void reset(const xml_document& proto);
+
+ #ifndef PUGIXML_NO_STL
+ // Load document from stream.
+ xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+ xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);
+ #endif
+
+ // Load document from zero-terminated string. No encoding conversions are applied.
+ xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
+
+ // Load document from file
+ xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+ xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+ // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.
+ xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+ // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
+ // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.
+ xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+ // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
+ // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).
+ xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+ // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).
+ void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+
+ #ifndef PUGIXML_NO_STL
+ // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).
+ void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+ void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const;
+ #endif
+
+ // Save XML to file
+ bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+ bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+
+ // Get document element
+ xml_node document_element() const;
+ };
+
+#ifndef PUGIXML_NO_XPATH
+ // XPath query return type
+ enum xpath_value_type
+ {
+ xpath_type_none, // Unknown type (query failed to compile)
+ xpath_type_node_set, // Node set (xpath_node_set)
+ xpath_type_number, // Number
+ xpath_type_string, // String
+ xpath_type_boolean // Boolean
+ };
+
+ // XPath parsing result
+ struct PUGIXML_CLASS xpath_parse_result
+ {
+ // Error message (0 if no error)
+ const char* error;
+
+ // Last parsed offset (in char_t units from string start)
+ ptrdiff_t offset;
+
+ // Default constructor, initializes object to failed state
+ xpath_parse_result();
+
+ // Cast to bool operator
+ operator bool() const;
+
+ // Get error description
+ const char* description() const;
+ };
+
+ // A single XPath variable
+ class PUGIXML_CLASS xpath_variable
+ {
+ friend class xpath_variable_set;
+
+ protected:
+ xpath_value_type _type;
+ xpath_variable* _next;
+
+ xpath_variable();
+
+ // Non-copyable semantics
+ xpath_variable(const xpath_variable&);
+ xpath_variable& operator=(const xpath_variable&);
+
+ public:
+ // Get variable name
+ const char_t* name() const;
+
+ // Get variable type
+ xpath_value_type type() const;
+
+ // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error
+ bool get_boolean() const;
+ double get_number() const;
+ const char_t* get_string() const;
+ const xpath_node_set& get_node_set() const;
+
+ // Set variable value; no type conversion is performed, false is returned on type mismatch error
+ bool set(bool value);
+ bool set(double value);
+ bool set(const char_t* value);
+ bool set(const xpath_node_set& value);
+ };
+
+ // A set of XPath variables
+ class PUGIXML_CLASS xpath_variable_set
+ {
+ private:
+ xpath_variable* _data[64];
+
+ // Non-copyable semantics
+ xpath_variable_set(const xpath_variable_set&);
+ xpath_variable_set& operator=(const xpath_variable_set&);
+
+ xpath_variable* find(const char_t* name) const;
+
+ public:
+ // Default constructor/destructor
+ xpath_variable_set();
+ ~xpath_variable_set();
+
+ // Add a new variable or get the existing one, if the types match
+ xpath_variable* add(const char_t* name, xpath_value_type type);
+
+ // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch
+ bool set(const char_t* name, bool value);
+ bool set(const char_t* name, double value);
+ bool set(const char_t* name, const char_t* value);
+ bool set(const char_t* name, const xpath_node_set& value);
+
+ // Get existing variable by name
+ xpath_variable* get(const char_t* name);
+ const xpath_variable* get(const char_t* name) const;
+ };
+
+ // A compiled XPath query object
+ class PUGIXML_CLASS xpath_query
+ {
+ private:
+ void* _impl;
+ xpath_parse_result _result;
+
+ typedef void (*unspecified_bool_type)(xpath_query***);
+
+ // Non-copyable semantics
+ xpath_query(const xpath_query&);
+ xpath_query& operator=(const xpath_query&);
+
+ public:
+ // Construct a compiled object from XPath expression.
+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.
+ explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
+
+ // Destructor
+ ~xpath_query();
+
+ // Get query expression return type
+ xpath_value_type return_type() const;
+
+ // Evaluate expression as boolean value in the specified context; performs type conversion if necessary.
+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+ bool evaluate_boolean(const xpath_node& n) const;
+
+ // Evaluate expression as double value in the specified context; performs type conversion if necessary.
+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+ double evaluate_number(const xpath_node& n) const;
+
+ #ifndef PUGIXML_NO_STL
+ // Evaluate expression as string value in the specified context; performs type conversion if necessary.
+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+ string_t evaluate_string(const xpath_node& n) const;
+ #endif
+
+ // Evaluate expression as string value in the specified context; performs type conversion if necessary.
+ // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).
+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+ // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead.
+ size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;
+
+ // Evaluate expression as node set in the specified context.
+ // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
+ // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
+ xpath_node_set evaluate_node_set(const xpath_node& n) const;
+
+ // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
+ const xpath_parse_result& result() const;
+
+ // Safe bool conversion operator
+ operator unspecified_bool_type() const;
+
+ // Borland C++ workaround
+ bool operator!() const;
+ };
+
+ #ifndef PUGIXML_NO_EXCEPTIONS
+ // XPath exception class
+ class PUGIXML_CLASS xpath_exception: public std::exception
+ {
+ private:
+ xpath_parse_result _result;
+
+ public:
+ // Construct exception from parse result
+ explicit xpath_exception(const xpath_parse_result& result);
+
+ // Get error message
+ virtual const char* what() const throw();
+
+ // Get parse result
+ const xpath_parse_result& result() const;
+ };
+ #endif
+
+ // XPath node class (either xml_node or xml_attribute)
+ class PUGIXML_CLASS xpath_node
+ {
+ private:
+ xml_node _node;
+ xml_attribute _attribute;
+
+ typedef void (*unspecified_bool_type)(xpath_node***);
+
+ public:
+ // Default constructor; constructs empty XPath node
+ xpath_node();
+
+ // Construct XPath node from XML node/attribute
+ xpath_node(const xml_node& node);
+ xpath_node(const xml_attribute& attribute, const xml_node& parent);
+
+ // Get node/attribute, if any
+ xml_node node() const;
+ xml_attribute attribute() const;
+
+ // Get parent of contained node/attribute
+ xml_node parent() const;
+
+ // Safe bool conversion operator
+ operator unspecified_bool_type() const;
+
+ // Borland C++ workaround
+ bool operator!() const;
+
+ // Comparison operators
+ bool operator==(const xpath_node& n) const;
+ bool operator!=(const xpath_node& n) const;
+ };
+
+#ifdef __BORLANDC__
+ // Borland C++ workaround
+ bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);
+ bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);
+#endif
+
+ // A fixed-size collection of XPath nodes
+ class PUGIXML_CLASS xpath_node_set
+ {
+ public:
+ // Collection type
+ enum type_t
+ {
+ type_unsorted, // Not ordered
+ type_sorted, // Sorted by document order (ascending)
+ type_sorted_reverse // Sorted by document order (descending)
+ };
+
+ // Constant iterator type
+ typedef const xpath_node* const_iterator;
+
+ // Default constructor. Constructs empty set.
+ xpath_node_set();
+
+ // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful
+ xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);
+
+ // Destructor
+ ~xpath_node_set();
+
+ // Copy constructor/assignment operator
+ xpath_node_set(const xpath_node_set& ns);
+ xpath_node_set& operator=(const xpath_node_set& ns);
+
+ // Get collection type
+ type_t type() const;
+
+ // Get collection size
+ size_t size() const;
+
+ // Indexing operator
+ const xpath_node& operator[](size_t index) const;
+
+ // Collection iterators
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ // Sort the collection in ascending/descending order by document order
+ void sort(bool reverse = false);
+
+ // Get first node in the collection by document order
+ xpath_node first() const;
+
+ // Check if collection is empty
+ bool empty() const;
+
+ private:
+ type_t _type;
+
+ xpath_node _storage;
+
+ xpath_node* _begin;
+ xpath_node* _end;
+
+ void _assign(const_iterator begin, const_iterator end);
+ };
+#endif
+
+#ifndef PUGIXML_NO_STL
+ // Convert wide string to UTF8
+ std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);
+ std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);
+
+ // Convert UTF8 to wide string
+ std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);
+ std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);
+#endif
+
+ // Memory allocation function interface; returns pointer to allocated memory or NULL on failure
+ typedef void* (*allocation_function)(size_t size);
+
+ // Memory deallocation function interface
+ typedef void (*deallocation_function)(void* ptr);
+
+ // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.
+ void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);
+
+ // Get current memory management functions
+ allocation_function PUGIXML_FUNCTION get_memory_allocation_function();
+ deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();
+}
+
+#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
+namespace std
+{
+ // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
+ std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
+ std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
+ std::forward_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
+}
+#endif
+
+#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
+namespace std
+{
+ // Workarounds for (non-standard) iterator category detection
+ std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
+ std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
+ std::forward_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
+}
+#endif
+
+#endif
+
+/**
+ * Copyright (c) 2006-2012 Arseny Kapoulkine
+ *
+ * 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.
+ */
diff --git a/src/test/DBusConnectionTest.cpp b/src/test/DBusConnectionTest.cpp
index ad51f38..b1e7f51 100644
--- a/src/test/DBusConnectionTest.cpp
+++ b/src/test/DBusConnectionTest.cpp
@@ -267,7 +267,7 @@ void notifyThunk(DBusPendingCall*, void* data) {
promise.set_value(true);
}
-TEST_F(DBusConnectionTest, DISABLED_LibdbusConnectionsMayCommitSuicide) {
+TEST_F(DBusConnectionTest, LibdbusConnectionsMayCommitSuicide) {
const ::DBusBusType libdbusType = ::DBusBusType::DBUS_BUS_SESSION;
::DBusError libdbusError;
dbus_error_init(&libdbusError);
@@ -322,6 +322,7 @@ void noPartnerCleanup(void* data) {
promise3.set_value(true);
}
+// libdbus bug
TEST_F(DBusConnectionTest, DISABLED_TimeoutForNonexistingServices) {
const ::DBusBusType libdbusType = ::DBusBusType::DBUS_BUS_SESSION;
::DBusError libdbusError;
@@ -362,32 +363,6 @@ TEST_F(DBusConnectionTest, DISABLED_TimeoutForNonexistingServices) {
dispatchThread.join();
}
-TEST_F(DBusConnectionTest, DISABLED_ConnectionsMayCommitAsynchronousSuicide) {
- CommonAPI::DBus::DBusConnection* dbusConnection_ = new CommonAPI::DBus::DBusConnection(
- CommonAPI::DBus::BusType::SESSION);
- dbusConnection_->connect();
-
- auto dbusMessageCall = CommonAPI::DBus::DBusMessage::createMethodCall(
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "ListNames",
- "");
-
- bool hasHappened = false;
-
- auto future = dbusConnection_->sendDBusMessageWithReplyAsync(
- dbusMessageCall,
- CommonAPI::DBus::DBusProxyAsyncCallbackHandler<std::vector<std::string>>::create(
- [&] (const CommonAPI::CallStatus&, std::vector<std::string>) {
- hasHappened = true;
- delete dbusConnection_;
- }
- ));
-
- ASSERT_EQ(CommonAPI::CallStatus::SUCCESS, future.get());
-}
-
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
diff --git a/src/test/DBusManagedTest.cpp b/src/test/DBusManagedTest.cpp
new file mode 100644
index 0000000..bbbcef2
--- /dev/null
+++ b/src/test/DBusManagedTest.cpp
@@ -0,0 +1,351 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _GLIBCXX_USE_NANOSLEEP
+#define _GLIBCXX_USE_NANOSLEEP
+#endif
+
+#include <CommonAPI/CommonAPI.h>
+
+#define COMMONAPI_INTERNAL_COMPILATION
+#include <CommonAPI/DBus/DBusObjectManagerStub.h>
+#include <CommonAPI/DBus/DBusConnection.h>
+#include <CommonAPI/DBus/DBusInputStream.h>
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusRuntime.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+
+#include <CommonAPI/ProxyManager.h>
+
+#include "commonapi/tests/managed/RootInterfaceStubDefault.h"
+#include "commonapi/tests/managed/LeafInterfaceStubDefault.h"
+
+#include "commonapi/tests/managed/RootInterfaceDBusProxy.h"
+#include "commonapi/tests/managed/LeafInterfaceProxy.h"
+
+
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <array>
+#include <memory>
+
+static const std::string rootAddress = "local:commonapi.tests.managed.RootInterface:commonapi.tests.managed.RootInterface";
+static const std::string leafInstance = "commonapi.tests.managed.RootInterface.LeafInterface";
+static const std::string leafAddress = "local:commonapi.tests.managed.LeafInterface:" + leafInstance;
+
+static const std::string dbusServiceName = "CommonAPI.DBus.DBusObjectManagerStubTest";
+static const std::string& dbusObjectManagerStubPath = "/commonapi/dbus/test/DBusObjectManagerStub";
+static const std::string& managedDBusObjectPathPrefix = "/commonapi/dbus/test/DBusObjectManagerStub/ManagedObject";
+
+
+class DBusManagedTest: public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ leafStatus = CommonAPI::AvailabilityStatus::UNKNOWN;
+ runtime = std::dynamic_pointer_cast<CommonAPI::DBus::DBusRuntime>(CommonAPI::Runtime::load());
+ serviceFactory = std::dynamic_pointer_cast<CommonAPI::DBus::DBusFactory>(runtime->createFactory());
+ clientFactory = std::dynamic_pointer_cast<CommonAPI::DBus::DBusFactory>(runtime->createFactory());
+
+ proxyDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
+ ASSERT_TRUE(proxyDBusConnection_->connect());
+
+ stubDBusConnection_ = serviceFactory->getDbusConnection();
+ ASSERT_TRUE(stubDBusConnection_->connect());
+ ASSERT_TRUE(bool(stubDBusConnection_->getDBusObjectManager()));
+ ASSERT_TRUE(stubDBusConnection_->requestServiceNameAndBlock(dbusServiceName));
+ }
+
+ virtual void TearDown() {
+ runtime->getServicePublisher()->unregisterService(rootAddress);
+
+ stubDBusConnection_->disconnect();
+ stubDBusConnection_.reset();
+
+ proxyDBusConnection_->disconnect();
+ proxyDBusConnection_.reset();
+ }
+
+ std::shared_ptr<CommonAPI::DBus::DBusRuntime> runtime;
+ std::shared_ptr<CommonAPI::DBus::DBusFactory> serviceFactory;
+ std::shared_ptr<CommonAPI::DBus::DBusFactory> clientFactory;
+
+ CommonAPI::AvailabilityStatus leafStatus;
+
+ void getManagedObjects(const std::string& dbusObjectPath,
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dbusObjectPathAndInterfacesDict) {
+ auto dbusMessageCall = CommonAPI::DBus::DBusMessage::createMethodCall(
+ dbusServiceName,
+ dbusObjectPath,
+ CommonAPI::DBus::DBusObjectManagerStub::getInterfaceName(),
+ "GetManagedObjects");
+
+ CommonAPI::DBus::DBusError dbusError;
+ auto dbusMessageReply = proxyDBusConnection_->sendDBusMessageWithReplyAndBlock(dbusMessageCall, dbusError);
+
+ ASSERT_FALSE(dbusError) << dbusError.getMessage();
+ ASSERT_TRUE(dbusMessageReply.isMethodReturnType());
+ ASSERT_TRUE(dbusMessageReply.hasSignature("a{oa{sa{sv}}}"));
+
+ CommonAPI::DBus::DBusInputStream dbusInputStream(dbusMessageReply);
+
+ dbusInputStream >> dbusObjectPathAndInterfacesDict;
+ ASSERT_FALSE(dbusInputStream.hasError());
+ }
+
+ void getIntrospectionData(const std::string& dbusObjectPath, std::string& introspectionDataXml) {
+ auto dbusMessageCall = CommonAPI::DBus::DBusMessage::createMethodCall(
+ dbusServiceName,
+ dbusObjectPath,
+ "org.freedesktop.DBus.Introspectable",
+ "Introspect");
+ CommonAPI::DBus::DBusError dbusError;
+ auto dbusMessageReply = proxyDBusConnection_->sendDBusMessageWithReplyAndBlock(dbusMessageCall, dbusError);
+
+ ASSERT_FALSE(dbusError) << dbusError.getMessage();
+ ASSERT_TRUE(dbusMessageReply.isMethodReturnType());
+ ASSERT_TRUE(dbusMessageReply.hasSignature("s"));
+
+ CommonAPI::DBus::DBusInputStream dbusInputStream(dbusMessageReply);
+
+ dbusInputStream >> introspectionDataXml;
+ ASSERT_FALSE(dbusInputStream.hasError());
+ }
+
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> proxyDBusConnection_;
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> stubDBusConnection_;
+ public:
+ //Callback on singnal...
+ void managedObjectSignalled(std::string address, CommonAPI::AvailabilityStatus status) {
+ //ASSERT_EQ(address, leafAddress);
+ leafStatus = status;
+ }
+
+
+};
+
+TEST_F(DBusManagedTest, RegisterRoot) {
+ auto rootStub = std::make_shared<commonapi::tests::managed::RootInterfaceStubDefault>();
+ runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory);
+
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+
+ EXPECT_FALSE(dbusObjectPathAndInterfacesDict.empty());
+
+ runtime->getServicePublisher()->unregisterService(rootAddress);
+}
+
+
+TEST_F(DBusManagedTest, RegisterLeafUnmanaged) {
+ auto leafStub = std::make_shared<commonapi::tests::managed::LeafInterfaceStubDefault>();
+ runtime->getServicePublisher()->registerService(leafStub, leafAddress, serviceFactory);
+
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+
+ EXPECT_FALSE(dbusObjectPathAndInterfacesDict.empty());
+
+ runtime->getServicePublisher()->unregisterService(leafAddress);
+}
+
+TEST_F(DBusManagedTest, RegisterLeafManaged) {
+ const char* rootInterfaceName = "/commonapi/tests/managed/RootInterface";
+ const char* rootObjectPath = "/commonapi/tests/managed/RootInterface";
+
+ auto rootStub = std::make_shared<commonapi::tests::managed::RootInterfaceStubDefault>();
+ runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory);
+
+ std::shared_ptr<commonapi::tests::managed::RootInterfaceDBusProxy> rootProxy = std::make_shared<
+ commonapi::tests::managed::RootInterfaceDBusProxy>(
+ clientFactory,
+ rootAddress,
+ rootInterfaceName,
+ dbusServiceName,
+ rootObjectPath,
+ proxyDBusConnection_
+ );
+ rootProxy->init();
+
+ for (uint32_t i = 0; !rootProxy->isAvailable() && i < 200; ++i) {
+ usleep(10 * 1000);
+ }
+ ASSERT_TRUE(rootProxy->isAvailable());
+
+ CommonAPI::ProxyManager& proxyManagerLeafInterface = rootProxy->getProxyManagerLeafInterface();
+ proxyManagerLeafInterface.getInstanceAvailabilityStatusChangedEvent().subscribe(
+ std::bind(&DBusManagedTest::managedObjectSignalled,
+ this,
+ std::placeholders::_1,
+ std::placeholders::_2));
+
+ auto leafStub = std::make_shared<commonapi::tests::managed::LeafInterfaceStubDefault>();
+
+ bool reg = rootStub->registerManagedStubLeafInterface(leafStub, leafInstance);
+ ASSERT_TRUE(reg);
+
+ for (uint32_t i = 0; leafStatus != CommonAPI::AvailabilityStatus::AVAILABLE && i < 50; ++i) {
+ usleep(1000);
+ }
+ ASSERT_TRUE(leafStatus == CommonAPI::AvailabilityStatus::AVAILABLE);
+
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_FALSE(dbusObjectPathAndInterfacesDict.empty());
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects(rootInterfaceName, dbusObjectPathAndInterfacesDict);
+ ASSERT_FALSE(dbusObjectPathAndInterfacesDict.empty());
+
+ bool deregistered = rootStub->deregisterManagedStubLeafInterface(leafInstance);
+ ASSERT_TRUE(deregistered);
+
+ for (uint32_t i = 0; leafStatus != CommonAPI::AvailabilityStatus::NOT_AVAILABLE && i < 200; ++i) {
+ usleep(10 * 1000);
+ }
+ ASSERT_TRUE(leafStatus == CommonAPI::AvailabilityStatus::NOT_AVAILABLE);
+
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects(rootInterfaceName, dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+
+ runtime->getServicePublisher()->unregisterService(rootAddress);
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+}
+
+TEST_F(DBusManagedTest, RegisterLeafManagedAndCreateProxyForLeaf) {
+ const char* rootInterfaceName = "/commonapi/tests/managed/RootInterface";
+ const char* rootObjectPath = "/commonapi/tests/managed/RootInterface";
+
+ auto rootStub = std::make_shared<commonapi::tests::managed::RootInterfaceStubDefault>();
+ runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory);
+
+ std::shared_ptr<commonapi::tests::managed::RootInterfaceDBusProxy> rootProxy = std::make_shared<
+ commonapi::tests::managed::RootInterfaceDBusProxy>(
+ clientFactory,
+ rootAddress,
+ rootInterfaceName,
+ dbusServiceName,
+ rootObjectPath,
+ proxyDBusConnection_
+ );
+ rootProxy->init();
+
+ for (uint32_t i = 0; !rootProxy->isAvailable() && i < 200; ++i) {
+ usleep(10 * 1000);
+ }
+ ASSERT_TRUE(rootProxy->isAvailable());
+
+ CommonAPI::ProxyManager& proxyManagerLeafInterface = rootProxy->getProxyManagerLeafInterface();
+ proxyManagerLeafInterface.getInstanceAvailabilityStatusChangedEvent().subscribe(std::bind(&DBusManagedTest::managedObjectSignalled, this, std::placeholders::_1, std::placeholders::_2));
+
+ auto leafStub = std::make_shared<commonapi::tests::managed::LeafInterfaceStubDefault>();
+ bool reg = rootStub->registerManagedStubLeafInterface(leafStub, leafInstance);
+ ASSERT_TRUE(reg);
+
+ sleep(2);
+
+ auto leafProxy = proxyManagerLeafInterface.buildProxy<commonapi::tests::managed::LeafInterfaceProxy>(leafInstance);
+ for (uint32_t i = 0; !leafProxy->isAvailable() && i < 500; ++i) {
+ usleep(10 * 1000);
+ }
+
+ ASSERT_TRUE(leafProxy->isAvailable());
+
+ CommonAPI::CallStatus callStatus;
+ commonapi::tests::managed::LeafInterface::testLeafMethodError error;
+ int outInt;
+ std::string outString;
+ leafProxy->testLeafMethod(42, "Test", callStatus, error, outInt, outString);
+
+ ASSERT_TRUE(callStatus == CommonAPI::CallStatus::SUCCESS);
+
+ bool dereg = rootStub->deregisterManagedStubLeafInterface(leafInstance);
+
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects(rootInterfaceName, dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+
+ runtime->getServicePublisher()->unregisterService(rootAddress);
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+}
+
+TEST_F(DBusManagedTest, PropagateTeardown) {
+ const char* rootInterfaceName = "/commonapi/tests/managed/RootInterface";
+ const char* rootObjectPath = "/commonapi/tests/managed/RootInterface";
+
+ auto rootStub = std::make_shared<commonapi::tests::managed::RootInterfaceStubDefault>();
+ runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory);
+
+
+ std::shared_ptr<commonapi::tests::managed::RootInterfaceDBusProxy> rootProxy = std::make_shared<
+ commonapi::tests::managed::RootInterfaceDBusProxy>(
+ clientFactory,
+ rootAddress,
+ rootInterfaceName,
+ dbusServiceName,
+ rootObjectPath,
+ proxyDBusConnection_
+ );
+ rootProxy->init();
+
+ for (uint32_t i = 0; !rootProxy->isAvailable() && i < 200; ++i) {
+ usleep(10 * 1000);
+ }
+ ASSERT_TRUE(rootProxy->isAvailable());
+
+ CommonAPI::ProxyManager& proxyManagerLeafInterface = rootProxy->getProxyManagerLeafInterface();
+ proxyManagerLeafInterface.getInstanceAvailabilityStatusChangedEvent().subscribe(std::bind(&DBusManagedTest::managedObjectSignalled, this, std::placeholders::_1, std::placeholders::_2));
+
+ auto leafStub = std::make_shared<commonapi::tests::managed::LeafInterfaceStubDefault>();
+ bool reg = rootStub->registerManagedStubLeafInterface(leafStub, leafInstance);
+ ASSERT_TRUE(reg);
+
+ sleep(2);
+
+ auto leafProxy = proxyManagerLeafInterface.buildProxy<commonapi::tests::managed::LeafInterfaceProxy>(leafInstance);
+
+ for (uint32_t i = 0; !leafProxy->isAvailable() && i < 500; ++i) {
+ usleep(10 * 1000);
+ }
+
+ ASSERT_TRUE(leafProxy->isAvailable());
+
+ CommonAPI::CallStatus callStatus;
+ commonapi::tests::managed::LeafInterface::testLeafMethodError error;
+ int outInt;
+ std::string outString;
+ leafProxy->testLeafMethod(42, "Test", callStatus, error, outInt, outString);
+
+ ASSERT_TRUE(callStatus == CommonAPI::CallStatus::SUCCESS);
+
+ bool dereg = runtime->getServicePublisher()->unregisterService(rootAddress);
+ ASSERT_TRUE(dereg);
+
+ for (uint32_t i = 0; leafStatus != CommonAPI::AvailabilityStatus::NOT_AVAILABLE && i < 100; ++i) {
+ usleep(10 * 1000);
+ }
+ ASSERT_TRUE(leafStatus == CommonAPI::AvailabilityStatus::NOT_AVAILABLE);
+
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+}
+
+//XXX: Needs tests for invalid instances for the children.
+//XXX: Also later on need auto generated instance ID test and
+//XXX: Also check transitive teardown
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/DBusObjectManagerStubTest.cpp b/src/test/DBusObjectManagerStubTest.cpp
new file mode 100644
index 0000000..1df20bf
--- /dev/null
+++ b/src/test/DBusObjectManagerStubTest.cpp
@@ -0,0 +1,472 @@
+/* Copyright (C) 2013 BMW Group
+ * Author: Manfred Bathelt (manfred.bathelt@bmw.de)
+ * Author: Juergen Gehring (juergen.gehring@bmw.de)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _GLIBCXX_USE_NANOSLEEP
+#define _GLIBCXX_USE_NANOSLEEP
+#endif
+
+#include <CommonAPI/CommonAPI.h>
+
+#define COMMONAPI_INTERNAL_COMPILATION
+#include <CommonAPI/DBus/DBusObjectManagerStub.h>
+#include <CommonAPI/DBus/DBusConnection.h>
+#include <CommonAPI/DBus/DBusInputStream.h>
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusRuntime.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <array>
+#include <memory>
+
+
+static const std::string dbusServiceName = "CommonAPI.DBus.DBusObjectManagerStubTest";
+static const std::string& dbusObjectManagerStubPath = "/commonapi/dbus/test/DBusObjectManagerStub";
+static const std::string& managedDBusObjectPathPrefix = "/commonapi/dbus/test/DBusObjectManagerStub/ManagedObject";
+
+
+class TestDBusStubAdapter: public CommonAPI::DBus::DBusStubAdapter {
+ public:
+ TestDBusStubAdapter(const std::shared_ptr<CommonAPI::DBus::DBusFactory> factory,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection) :
+ DBusStubAdapter(factory, "local:" + dbusServiceName + ":commonapi.dbus.tests.TestDBusStubAdapter-" + dbusObjectPath,
+ "commonapi.dbus.tests.TestDBusStubAdapter",
+ dbusServiceName,
+ dbusObjectPath,
+ dbusConnection) {
+ }
+
+ void deactivateManagedInstances() {
+
+ }
+
+ virtual const char* getMethodsDBusIntrospectionXmlData() const {
+ return "";
+ }
+
+ virtual bool onInterfaceDBusMessage(const CommonAPI::DBus::DBusMessage& dbusMessage) {
+ return false;
+ }
+
+
+
+ protected:
+ TestDBusStubAdapter(const std::shared_ptr<CommonAPI::DBus::DBusFactory> factory,
+ const std::string& dbusObjectPath,
+ const std::string& dbusInterfaceName,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection) :
+ DBusStubAdapter(factory, "local:" + dbusServiceName + ":" + dbusInterfaceName + "-" + dbusObjectPath,
+ dbusInterfaceName,
+ dbusServiceName,
+ dbusObjectPath,
+ dbusConnection) {
+ }
+
+
+};
+
+
+class ManagerTestDBusStubAdapter: public TestDBusStubAdapter {
+ public:
+ ManagerTestDBusStubAdapter(const std::shared_ptr<CommonAPI::DBus::DBusFactory> factory, const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection) :
+ TestDBusStubAdapter(factory, dbusObjectPath, "commonapi.dbus.tests.ManagerTestDBusStubAdapter", dbusConnection),
+ dbusObjectManagerStub_(dbusObjectPath, dbusConnection) {
+ }
+
+ virtual CommonAPI::DBus::DBusObjectManagerStub* getDBusObjectManagerStub() {
+ return &dbusObjectManagerStub_;
+ }
+
+ private:
+ CommonAPI::DBus::DBusObjectManagerStub dbusObjectManagerStub_;
+};
+
+
+struct TestDBusObjectManagerSignalHandler: public CommonAPI::DBus::DBusConnection::DBusSignalHandler {
+ size_t totalAddedCount;
+ size_t totalRemovedCount;
+
+ std::string lastAddedDBusObjectPath;
+ std::string lastRemovedDBusObjectPath;
+
+ std::condition_variable signalReceived;
+ std::mutex lock;
+
+
+ ~TestDBusObjectManagerSignalHandler() {
+ dbusConnection_->removeSignalMemberHandler(dbusSignalHandlerAddedToken_);
+ dbusConnection_->removeSignalMemberHandler(dbusSignalHandlerRemovedToken_);
+ }
+
+ virtual CommonAPI::SubscriptionStatus onSignalDBusMessage(const CommonAPI::DBus::DBusMessage& dbusMessage) {
+ if (!dbusMessage.hasInterfaceName(CommonAPI::DBus::DBusObjectManagerStub::getInterfaceName())) {
+ return CommonAPI::SubscriptionStatus::CANCEL;
+ }
+
+ if (!dbusMessage.hasMemberName("InterfacesAdded") && !dbusMessage.hasMemberName("InterfacesRemoved")) {
+ return CommonAPI::SubscriptionStatus::CANCEL;
+ }
+
+ CommonAPI::DBus::DBusInputStream dbusInputStream(dbusMessage);
+ std::lock_guard<std::mutex> lockGuard(lock);
+
+ if (dbusMessage.hasMemberName("InterfacesAdded")) {
+ totalAddedCount++;
+ dbusInputStream >> lastAddedDBusObjectPath;
+ } else {
+ totalRemovedCount++;
+ dbusInputStream >> lastRemovedDBusObjectPath;
+ }
+
+ signalReceived.notify_all();
+
+ return CommonAPI::SubscriptionStatus::RETAIN;
+ }
+
+ static inline std::shared_ptr<TestDBusObjectManagerSignalHandler> create(
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection) {
+ auto dbusSignalHandler = new TestDBusObjectManagerSignalHandler(dbusObjectPath, dbusConnection);
+ dbusSignalHandler->init();
+ return std::shared_ptr<TestDBusObjectManagerSignalHandler>(dbusSignalHandler);
+ }
+
+ private:
+ TestDBusObjectManagerSignalHandler(const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection) :
+ dbusObjectPath_(dbusObjectPath),
+ dbusConnection_(dbusConnection),
+ totalAddedCount(0),
+ totalRemovedCount(0) {
+ }
+
+ void init() {
+ dbusSignalHandlerAddedToken_ = dbusConnection_->addSignalMemberHandler(
+ dbusObjectPath_,
+ CommonAPI::DBus::DBusObjectManagerStub::getInterfaceName(),
+ "InterfacesAdded",
+ "oa{sa{sv}}",
+ this);
+
+ dbusSignalHandlerRemovedToken_ = dbusConnection_->addSignalMemberHandler(
+ dbusObjectPath_,
+ CommonAPI::DBus::DBusObjectManagerStub::getInterfaceName(),
+ "InterfacesRemoved",
+ "oas",
+ this);
+ }
+
+ std::string dbusObjectPath_;
+ std::shared_ptr<CommonAPI::DBus::DBusProxyConnection> dbusConnection_;
+ CommonAPI::DBus::DBusProxyConnection::DBusSignalHandlerToken dbusSignalHandlerAddedToken_;
+ CommonAPI::DBus::DBusProxyConnection::DBusSignalHandlerToken dbusSignalHandlerRemovedToken_;
+};
+
+
+class DBusObjectManagerStubTest: public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ auto runtime = std::dynamic_pointer_cast<CommonAPI::DBus::DBusRuntime>(CommonAPI::Runtime::load());
+ serviceFactory = std::dynamic_pointer_cast<CommonAPI::DBus::DBusFactory>(runtime->createFactory());
+
+ proxyDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
+ ASSERT_TRUE(proxyDBusConnection_->connect());
+
+ stubDBusConnection_ = serviceFactory->getDbusConnection();
+ ASSERT_TRUE(stubDBusConnection_->connect());
+ ASSERT_TRUE(bool(stubDBusConnection_->getDBusObjectManager()));
+ ASSERT_TRUE(stubDBusConnection_->requestServiceNameAndBlock(dbusServiceName));
+ }
+
+ virtual void TearDown() {
+ stubDBusConnection_->disconnect();
+ stubDBusConnection_.reset();
+
+ proxyDBusConnection_->disconnect();
+ proxyDBusConnection_.reset();
+ }
+
+ std::shared_ptr<CommonAPI::DBus::DBusFactory> serviceFactory;
+
+ void getManagedObjects(const std::string& dbusObjectPath,
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dbusObjectPathAndInterfacesDict) {
+ auto dbusMessageCall = CommonAPI::DBus::DBusMessage::createMethodCall(
+ dbusServiceName,
+ dbusObjectPath,
+ CommonAPI::DBus::DBusObjectManagerStub::getInterfaceName(),
+ "GetManagedObjects");
+
+ CommonAPI::DBus::DBusError dbusError;
+ auto dbusMessageReply = proxyDBusConnection_->sendDBusMessageWithReplyAndBlock(dbusMessageCall, dbusError);
+
+ ASSERT_FALSE(dbusError) << dbusError.getMessage();
+ ASSERT_TRUE(dbusMessageReply.isMethodReturnType());
+ ASSERT_TRUE(dbusMessageReply.hasSignature("a{oa{sa{sv}}}"));
+
+ CommonAPI::DBus::DBusInputStream dbusInputStream(dbusMessageReply);
+
+ dbusInputStream >> dbusObjectPathAndInterfacesDict;
+ ASSERT_FALSE(dbusInputStream.hasError());
+ }
+
+ void getIntrospectionData(const std::string& dbusObjectPath, std::string& introspectionDataXml) {
+ auto dbusMessageCall = CommonAPI::DBus::DBusMessage::createMethodCall(
+ dbusServiceName,
+ dbusObjectPath,
+ "org.freedesktop.DBus.Introspectable",
+ "Introspect");
+ CommonAPI::DBus::DBusError dbusError;
+ auto dbusMessageReply = proxyDBusConnection_->sendDBusMessageWithReplyAndBlock(dbusMessageCall, dbusError);
+
+ ASSERT_FALSE(dbusError) << dbusError.getMessage();
+ ASSERT_TRUE(dbusMessageReply.isMethodReturnType());
+ ASSERT_TRUE(dbusMessageReply.hasSignature("s"));
+
+ CommonAPI::DBus::DBusInputStream dbusInputStream(dbusMessageReply);
+
+ dbusInputStream >> introspectionDataXml;
+ ASSERT_FALSE(dbusInputStream.hasError());
+ }
+
+ void waitForInterfacesAdded(const std::shared_ptr<TestDBusObjectManagerSignalHandler>& dbusSignalHandler,
+ const size_t& interfacesAddedCount,
+ const size_t& interfacesRemovedExpectedCount) {
+ const size_t waitMillisecondsPerInterface = 300;
+
+ std::unique_lock<std::mutex> lock(dbusSignalHandler->lock);
+ auto waitResult = dbusSignalHandler->signalReceived.wait_for(
+ lock,
+ std::chrono::milliseconds(interfacesAddedCount * waitMillisecondsPerInterface),
+ [&]() { return dbusSignalHandler->totalAddedCount == interfacesAddedCount; });
+ ASSERT_TRUE(waitResult);
+ ASSERT_EQ(dbusSignalHandler->totalRemovedCount, interfacesRemovedExpectedCount);
+
+ const std::string lastAddedDBusObjectPath = managedDBusObjectPathPrefix + std::to_string(interfacesAddedCount - 1);
+ ASSERT_TRUE(dbusSignalHandler->lastAddedDBusObjectPath == lastAddedDBusObjectPath);
+ }
+
+ void waitForInterfacesRemoved(const std::shared_ptr<TestDBusObjectManagerSignalHandler>& dbusSignalHandler,
+ const size_t& interfacesRemovedCount,
+ const size_t& interfacesAddedExpectedCount) {
+ const size_t waitMillisecondsPerInterface = 300;
+
+ std::unique_lock<std::mutex> lock(dbusSignalHandler->lock);
+ auto waitResult = dbusSignalHandler->signalReceived.wait_for(
+ lock,
+ std::chrono::milliseconds(interfacesRemovedCount * waitMillisecondsPerInterface),
+ [&]() { return dbusSignalHandler->totalRemovedCount == interfacesRemovedCount; });
+ ASSERT_TRUE(waitResult);
+ ASSERT_EQ(dbusSignalHandler->totalAddedCount, interfacesAddedExpectedCount);
+
+ const std::string lastRemovedDBusObjectPath = managedDBusObjectPathPrefix + std::to_string(interfacesRemovedCount - 1);
+ ASSERT_TRUE(dbusSignalHandler->lastRemovedDBusObjectPath == lastRemovedDBusObjectPath);
+ }
+
+ template <typename _StubType, size_t _ArraySize>
+ void createDBusStubAdapter(std::array<std::shared_ptr<_StubType>, _ArraySize>& dbusStubAdapter) {
+ for (size_t i = 0; i < _ArraySize; i++) {
+ dbusStubAdapter[i] = std::make_shared<_StubType>(serviceFactory,
+ managedDBusObjectPathPrefix + std::to_string(i),
+ stubDBusConnection_);
+
+ ASSERT_TRUE(bool(dbusStubAdapter[i]));
+
+ dbusStubAdapter[i]->init();
+ }
+ }
+
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> proxyDBusConnection_;
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> stubDBusConnection_;
+};
+
+TEST_F(DBusObjectManagerStubTest, EmptyRootGetManagedObjectsWorks) {
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+}
+
+TEST_F(DBusObjectManagerStubTest, RootObjectManagerIntrospectionWorks) {
+ std::string introspectionDataXml;
+
+ getIntrospectionData("/", introspectionDataXml);
+
+ ASSERT_FALSE(introspectionDataXml.empty());
+ ASSERT_TRUE(introspectionDataXml.find("GetManagedObjects") != std::string::npos);
+ ASSERT_TRUE(introspectionDataXml.find("InterfacesAdded") != std::string::npos);
+ ASSERT_TRUE(introspectionDataXml.find("InterfacesRemoved") != std::string::npos);
+}
+
+TEST_F(DBusObjectManagerStubTest, RootRegisterStubAdapterWorks) {
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ auto dbusSignalHandler = TestDBusObjectManagerSignalHandler::create("/", proxyDBusConnection_);
+ std::array<std::shared_ptr<TestDBusStubAdapter>, 10> dbusStubAdapterArray;
+
+ createDBusStubAdapter(dbusStubAdapterArray);
+
+ const bool isServiceRegistrationSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(dbusStubAdapter);
+ });
+ ASSERT_TRUE(isServiceRegistrationSuccessful);
+
+ waitForInterfacesAdded(dbusSignalHandler, dbusStubAdapterArray.size(), 0);
+
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict.size(), dbusStubAdapterArray.size());
+
+ const bool isServiceDeregistrationSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ const auto& serviceAddress = dbusStubAdapter->getAddress();
+ return CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(serviceAddress);
+ });
+ ASSERT_TRUE(isServiceDeregistrationSuccessful);
+
+ waitForInterfacesRemoved(dbusSignalHandler, dbusStubAdapterArray.size(), dbusStubAdapterArray.size());
+
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+}
+
+TEST_F(DBusObjectManagerStubTest, RegisterManagerStubAdapterWorks) {
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ auto managerDBusStubAdapter = std::make_shared<ManagerTestDBusStubAdapter>(serviceFactory, dbusObjectManagerStubPath, stubDBusConnection_);
+ managerDBusStubAdapter->init();
+
+ ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(managerDBusStubAdapter));
+
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+
+ ASSERT_FALSE(dbusObjectPathAndInterfacesDict.empty());
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict.size(), 1);
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict.count(dbusObjectManagerStubPath), 1);
+
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict[dbusObjectManagerStubPath].size(), 2);
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict[dbusObjectManagerStubPath].count(CommonAPI::DBus::DBusObjectManagerStub::getInterfaceName()), 1);
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict[dbusObjectManagerStubPath].count(managerDBusStubAdapter->getInterfaceName()), 1);
+
+ ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(managerDBusStubAdapter->getAddress()));
+
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects("/", dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+}
+
+
+TEST_F(DBusObjectManagerStubTest, ManagerStubAdapterExportAndUnexportWorks) {
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ auto dbusSignalHandler = TestDBusObjectManagerSignalHandler::create(dbusObjectManagerStubPath, proxyDBusConnection_);
+ auto managerDBusStubAdapter = std::make_shared<ManagerTestDBusStubAdapter>(serviceFactory, dbusObjectManagerStubPath, stubDBusConnection_);
+ managerDBusStubAdapter->init();
+
+ ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(managerDBusStubAdapter));
+
+ std::array<std::shared_ptr<TestDBusStubAdapter>, 10> dbusStubAdapterArray;
+ createDBusStubAdapter(dbusStubAdapterArray);
+
+ const bool isServiceRegistrationSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return CommonAPI::DBus::DBusServicePublisher::getInstance()->registerManagedService(dbusStubAdapter);
+ });
+ ASSERT_TRUE(isServiceRegistrationSuccessful);
+
+ const bool isServiceExportSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [&](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return managerDBusStubAdapter->getDBusObjectManagerStub()->exportDBusStubAdapter(dbusStubAdapter.get());
+ });
+ ASSERT_TRUE(isServiceExportSuccessful);
+
+ waitForInterfacesAdded(dbusSignalHandler, dbusStubAdapterArray.size(), 0);
+
+ getManagedObjects(dbusObjectManagerStubPath, dbusObjectPathAndInterfacesDict);
+ ASSERT_EQ(dbusObjectPathAndInterfacesDict.size(), dbusStubAdapterArray.size());
+
+ const bool isServiceUnexportSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [&](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return managerDBusStubAdapter->getDBusObjectManagerStub()->unexportDBusStubAdapter(dbusStubAdapter.get());
+ });
+ ASSERT_TRUE(isServiceUnexportSuccessful);
+
+ const bool isServiceDeregistrationSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterManagedService(
+ dbusStubAdapter->getAddress());
+ });
+ ASSERT_TRUE(isServiceDeregistrationSuccessful);
+
+ waitForInterfacesRemoved(dbusSignalHandler, dbusStubAdapterArray.size(), dbusStubAdapterArray.size());
+
+ dbusObjectPathAndInterfacesDict.clear();
+ getManagedObjects(dbusObjectManagerStubPath, dbusObjectPathAndInterfacesDict);
+ ASSERT_TRUE(dbusObjectPathAndInterfacesDict.empty());
+
+ ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(managerDBusStubAdapter->getAddress()));
+}
+
+TEST_F(DBusObjectManagerStubTest, DestructorUnpublishingWorks) {
+ CommonAPI::DBus::DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ auto dbusSignalHandler = TestDBusObjectManagerSignalHandler::create(dbusObjectManagerStubPath, proxyDBusConnection_);
+ auto managerDBusStubAdapter = std::make_shared<ManagerTestDBusStubAdapter>(serviceFactory, dbusObjectManagerStubPath, stubDBusConnection_);
+ managerDBusStubAdapter->init();
+
+ ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(managerDBusStubAdapter));
+
+ std::array<std::shared_ptr<TestDBusStubAdapter>, 10> dbusStubAdapterArray;
+ createDBusStubAdapter(dbusStubAdapterArray);
+
+ const bool isServiceRegistrationSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return CommonAPI::DBus::DBusServicePublisher::getInstance()->registerManagedService(dbusStubAdapter);
+ });
+ ASSERT_TRUE(isServiceRegistrationSuccessful);
+
+ const bool isServiceExportSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [&](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return managerDBusStubAdapter->getDBusObjectManagerStub()->exportDBusStubAdapter(dbusStubAdapter.get());
+ });
+ ASSERT_TRUE(isServiceExportSuccessful);
+
+ waitForInterfacesAdded(dbusSignalHandler, dbusStubAdapterArray.size(), 0);
+
+ ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(managerDBusStubAdapter->getAddress()));
+ managerDBusStubAdapter.reset();
+
+ const bool wasServiceDeregistrationSuccessful = std::all_of(
+ dbusStubAdapterArray.begin(),
+ dbusStubAdapterArray.end(),
+ [](const std::shared_ptr<TestDBusStubAdapter>& dbusStubAdapter) {
+ return !CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterManagedService(
+ dbusStubAdapter->getAddress());
+ });
+ ASSERT_TRUE(wasServiceDeregistrationSuccessful);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/test/DBusProxyTest.cpp b/src/test/DBusProxyTest.cpp
index 3fc2c23..33e44d9 100644
--- a/src/test/DBusProxyTest.cpp
+++ b/src/test/DBusProxyTest.cpp
@@ -16,7 +16,10 @@
#include <CommonAPI/DBus/DBusMessage.h>
#include <CommonAPI/DBus/DBusProxy.h>
#include <CommonAPI/DBus/DBusConnection.h>
+#include <CommonAPI/DBus/DBusRuntime.h>
#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+#include <CommonAPI/DBus/DBusFactory.h>
#include <commonapi/tests/TestInterfaceDBusProxy.h>
#include <commonapi/tests/TestInterfaceDBusStubAdapter.h>
@@ -49,17 +52,26 @@ class ProxyTest: public ::testing::Test {
protected:
void SetUp() {
+
+ auto runtime = std::dynamic_pointer_cast<CommonAPI::DBus::DBusRuntime>(CommonAPI::Runtime::load());
+
+ serviceFactory = std::dynamic_pointer_cast<CommonAPI::DBus::DBusFactory>(runtime->createFactory());
+
proxyDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus();
ASSERT_TRUE(proxyDBusConnection_->connect());
proxy_ = std::make_shared<commonapi::tests::TestInterfaceDBusProxy>(
+ serviceFactory,
commonApiAddress,
interfaceName,
busName,
objectPath,
proxyDBusConnection_);
+ proxy_->init();
}
+ std::shared_ptr<CommonAPI::DBus::DBusFactory> serviceFactory;
+
virtual void TearDown() {
usleep(30000);
}
@@ -70,6 +82,7 @@ protected:
auto stubDefault = std::make_shared<commonapi::tests::TestInterfaceStubDefault>();
stubAdapter_ = std::make_shared<commonapi::tests::TestInterfaceDBusStubAdapter>(
+ serviceFactory,
commonApiAddress,
interfaceName,
busName,
@@ -78,18 +91,16 @@ protected:
stubDefault);
stubAdapter_->init();
- bool serviceNameAcquired = stubDBusConnection_->requestServiceNameAndBlock(busName);
+ const bool isStubAdapterRegistered = CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(stubAdapter_);
+ ASSERT_TRUE(isStubAdapterRegistered);
- for(unsigned int i = 0; !serviceNameAcquired && i < 100; i++) {
- usleep(10000);
- serviceNameAcquired = stubDBusConnection_->requestServiceNameAndBlock(busName);
- }
- ASSERT_TRUE(serviceNameAcquired);
usleep(500000);
}
void deregisterTestStub() {
- stubAdapter_->deinit();
+ const bool isStubAdapterUnregistered = CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(
+ stubAdapter_->getAddress());
+ ASSERT_TRUE(isStubAdapterUnregistered);
stubAdapter_.reset();
if (stubDBusConnection_->isConnected()) {
@@ -132,33 +143,33 @@ protected:
};
TEST_F(ProxyTest, HasCorrectConnectionName) {
- std::string actualName = proxy_->getDBusBusName();
- EXPECT_EQ(busName, actualName);
+ std::string actualName = proxy_->getDBusBusName();
+ EXPECT_EQ(busName, actualName);
}
TEST_F(ProxyTest, HasCorrectObjectPath) {
- std::string actualPath = proxy_->getDBusObjectPath();
- EXPECT_EQ(objectPath, actualPath);
+ std::string actualPath = proxy_->getDBusObjectPath();
+ EXPECT_EQ(objectPath, actualPath);
}
TEST_F(ProxyTest, HasCorrectInterfaceName) {
- std::string actualName = proxy_->getInterfaceName();
- EXPECT_EQ(interfaceName, actualName);
+ std::string actualName = proxy_->getInterfaceName();
+ EXPECT_EQ(interfaceName, actualName);
}
TEST_F(ProxyTest, IsNotAvailable) {
- bool isAvailable = proxy_->isAvailable();
- EXPECT_FALSE(isAvailable);
+ bool isAvailable = proxy_->isAvailable();
+ EXPECT_FALSE(isAvailable);
}
TEST_F(ProxyTest, IsConnected) {
- ASSERT_TRUE(proxy_->getDBusConnection()->isConnected());
+ ASSERT_TRUE(proxy_->getDBusConnection()->isConnected());
}
TEST_F(ProxyTest, AssociatedConnectionHasServiceRegistry) {
- std::shared_ptr<CommonAPI::DBus::DBusProxyConnection> connection = proxy_->getDBusConnection();
- auto registry = connection->getDBusServiceRegistry();
- ASSERT_FALSE(!registry);
+ std::shared_ptr<CommonAPI::DBus::DBusProxyConnection> connection = proxy_->getDBusConnection();
+ auto registry = connection->getDBusServiceRegistry();
+ ASSERT_FALSE(!registry);
}
TEST_F(ProxyTest, DBusProxyStatusEventBeforeServiceIsRegistered) {
@@ -213,10 +224,10 @@ TEST_F(ProxyTest, ServiceStatus) {
}
}
- auto found = std::find(availableDBusServices.begin(), availableDBusServices.end(), commonApiAddress);
+ auto found = std::find(availableDBusServices.begin(), availableDBusServices.end(), commonApiAddress);
- EXPECT_TRUE(availableDBusServices.begin() != availableDBusServices.end());
- EXPECT_TRUE(found != availableDBusServices.end());
+ EXPECT_TRUE(availableDBusServices.begin() != availableDBusServices.end());
+ EXPECT_TRUE(found != availableDBusServices.end());
deregisterTestStub();
}
@@ -250,16 +261,16 @@ TEST_F(ProxyTest, IsAvailableBlocking) {
}
TEST_F(ProxyTest, HasNecessaryAttributesAndEvents) {
- CommonAPI::InterfaceVersionAttribute& versionAttribute = (proxy_->getInterfaceVersionAttribute());
- CommonAPI::ProxyStatusEvent& statusEvent = (proxy_->getProxyStatusEvent());
+ CommonAPI::InterfaceVersionAttribute& versionAttribute = (proxy_->getInterfaceVersionAttribute());
+ CommonAPI::ProxyStatusEvent& statusEvent = (proxy_->getProxyStatusEvent());
}
TEST_F(ProxyTest, TestInterfaceVersionAttribute) {
- CommonAPI::InterfaceVersionAttribute& versionAttribute = proxy_->getInterfaceVersionAttribute();
- CommonAPI::Version version;
- CommonAPI::CallStatus status;
- ASSERT_NO_THROW(versionAttribute.getValue(status, version));
- ASSERT_EQ(CommonAPI::CallStatus::NOT_AVAILABLE, status);
+ CommonAPI::InterfaceVersionAttribute& versionAttribute = proxy_->getInterfaceVersionAttribute();
+ CommonAPI::Version version;
+ CommonAPI::CallStatus status;
+ ASSERT_NO_THROW(versionAttribute.getValue(status, version));
+ ASSERT_EQ(CommonAPI::CallStatus::NOT_AVAILABLE, status);
}
TEST_F(ProxyTest, AsyncCallbacksAreCalledIfServiceNotAvailable) {
@@ -267,17 +278,18 @@ TEST_F(ProxyTest, AsyncCallbacksAreCalledIfServiceNotAvailable) {
commonapi::tests::DerivedTypeCollection::TestMap testInputMap;
std::promise<bool> wasCalledPromise;
std::future<bool> wasCalledFuture = wasCalledPromise.get_future();
- proxy_->testDerivedTypeMethodAsync(testInputStruct, testInputMap, [&] (const CommonAPI::CallStatus& callStatus,
- const commonapi::tests::DerivedTypeCollection::TestEnumExtended2&,
- const commonapi::tests::DerivedTypeCollection::TestMap&) {
- ASSERT_EQ(callStatus, CommonAPI::CallStatus::NOT_AVAILABLE);
- wasCalledPromise.set_value(true);
- }
- );
+ proxy_->testDerivedTypeMethodAsync(testInputStruct, testInputMap, [&] (
+ const CommonAPI::CallStatus& callStatus,
+ const commonapi::tests::DerivedTypeCollection::TestEnumExtended2&,
+ const commonapi::tests::DerivedTypeCollection::TestMap&) {
+ ASSERT_EQ(callStatus, CommonAPI::CallStatus::NOT_AVAILABLE);
+ wasCalledPromise.set_value(true);
+ }
+ );
ASSERT_TRUE(wasCalledFuture.get());
}
int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/src/test/DBusSelectiveBroadcastTest.cpp b/src/test/DBusSelectiveBroadcastTest.cpp
index 4b2cb25..6585aab 100644
--- a/src/test/DBusSelectiveBroadcastTest.cpp
+++ b/src/test/DBusSelectiveBroadcastTest.cpp
@@ -70,9 +70,10 @@ public:
void sendToLastSubscribedClient()
{
sentBroadcasts++;
- CommonAPI::ClientIdList receivers = { lastSubscribedClient };
+ std::shared_ptr<CommonAPI::ClientIdList> receivers = std::make_shared<CommonAPI::ClientIdList>();
+ receivers->insert(lastSubscribedClient);
- fireTestSelectiveBroadcastSelective(&receivers);
+ fireTestSelectiveBroadcastSelective(receivers);
}
diff --git a/src/test/DBusServiceRegistryTest.cpp b/src/test/DBusServiceRegistryTest.cpp
index d5d2700..a429291 100644
--- a/src/test/DBusServiceRegistryTest.cpp
+++ b/src/test/DBusServiceRegistryTest.cpp
@@ -17,8 +17,10 @@
#endif
#include <CommonAPI/DBus/DBusServiceRegistry.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
#include <CommonAPI/DBus/DBusConnection.h>
#include <CommonAPI/DBus/DBusUtils.h>
+#include <CommonAPI/DBus/DBusRuntime.h>
#include <commonapi/tests/TestInterfaceStub.h>
#include <commonapi/tests/TestInterfaceStubDefault.h>
@@ -28,6 +30,9 @@
#include "DemoMainLoop.h"
+#include <fstream>
+#include <chrono>
+
// all predefinedInstances will be added for this service
static const std::string dbusServiceName = "DBusServiceRegistryTest.Predefined.Service";
@@ -76,29 +81,244 @@ public:
};
+struct TestDBusServiceListener {
+ CommonAPI::AvailabilityStatus lastAvailabilityStatus;
+ size_t availabilityStatusCount;
+
+ std::condition_variable statusChanged;
+ std::mutex lock;
+
+ TestDBusServiceListener(const std::string& commonApiAddress,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection):
+ commonApiAddress_(commonApiAddress),
+ dbusServiceRegistry_(dbusConnection->getDBusServiceRegistry()),
+ isSubscribed(false),
+ availabilityStatusCount(0) {
+ }
+
+ ~TestDBusServiceListener() {
+ if (isSubscribed) {
+ unsubscribe();
+ }
+ }
+
+ void subscribe() {
+ ASSERT_TRUE(!isSubscribed);
+
+ dbusServiceSubscription_= dbusServiceRegistry_->subscribeAvailabilityListener(
+ commonApiAddress_,
+ [&](const CommonAPI::AvailabilityStatus& availabilityStatus) {
+
+ std::lock_guard<std::mutex> lockGuard(lock);
+
+ lastAvailabilityStatus = availabilityStatus;
+ availabilityStatusCount++;
+
+ statusChanged.notify_all();
+
+ return CommonAPI::SubscriptionStatus::RETAIN;
+ });
+
+ isSubscribed = true;
+ }
+
+ void unsubscribe() {
+ ASSERT_TRUE(isSubscribed);
+
+ dbusServiceRegistry_->unsubscribeAvailabilityListener(
+ commonApiAddress_,
+ dbusServiceSubscription_);
+ }
+
+ private:
+ bool isSubscribed;
+ std::string commonApiAddress_;
+ std::shared_ptr<CommonAPI::DBus::DBusServiceRegistry> dbusServiceRegistry_;
+ CommonAPI::DBus::DBusServiceRegistry::DBusServiceSubscription dbusServiceSubscription_;
+};
+
+
+class TestDBusStubAdapter: public CommonAPI::DBus::DBusStubAdapter {
+ public:
+ TestDBusStubAdapter(const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection):
+ DBusStubAdapter(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, dbusConnection),
+ introspectionCount(0) {
+ }
+
+ void deactivateManagedInstances() {
+
+ }
+
+ virtual const char* getMethodsDBusIntrospectionXmlData() const {
+ introspectionCount++;
+ return "";
+ }
+
+ virtual bool onInterfaceDBusMessage(const CommonAPI::DBus::DBusMessage& dbusMessage) {
+ return false;
+ }
+
+ mutable size_t introspectionCount;
+};
+
class DBusServiceRegistryTest: public ::testing::Test {
protected:
virtual void SetUp() {
+
+ auto runtime = std::dynamic_pointer_cast<CommonAPI::DBus::DBusRuntime>(CommonAPI::Runtime::load());
+
+ clientFactory = std::dynamic_pointer_cast<CommonAPI::DBus::DBusFactory>(runtime->createFactory());
+ serviceFactory = std::dynamic_pointer_cast<CommonAPI::DBus::DBusFactory>(runtime->createFactory());
+
+ clientDBusConnection = clientFactory->getDbusConnection();
+ clientConnectionRegistry = clientDBusConnection->getDBusServiceRegistry();
+ serviceDBusConnection = serviceFactory->getDbusConnection();
+ }
+
+ virtual void TearDown() {
+ if (clientDBusConnection && clientDBusConnection->isConnected()) {
+ clientDBusConnection->disconnect();
+ }
+
+ if (serviceDBusConnection && serviceDBusConnection->isConnected()) {
+ serviceDBusConnection->disconnect();
+ }
}
- virtual void TearDown() {
- }
+ bool waitForAvailabilityStatusChanged(TestDBusServiceListener& testDBusServiceListener,
+ const CommonAPI::AvailabilityStatus& availabilityStatus) {
+ std::unique_lock<std::mutex> lock(testDBusServiceListener.lock);
+ auto waitResult =
+ testDBusServiceListener.statusChanged.wait_for(
+ lock,
+ std::chrono::milliseconds(4000),
+ [&]() {return testDBusServiceListener.lastAvailabilityStatus == availabilityStatus;});
+ return waitResult;
+ }
+
+ std::shared_ptr<CommonAPI::DBus::DBusFactory> clientFactory;
+ std::shared_ptr<CommonAPI::DBus::DBusFactory> serviceFactory;
+
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> clientDBusConnection;
+ std::shared_ptr<CommonAPI::DBus::DBusConnection> serviceDBusConnection;
+
+ std::shared_ptr<CommonAPI::DBus::DBusServiceRegistry> clientConnectionRegistry;
+ //std::shared_ptr<CommonAPI::DBus::DBusServiceRegistry> serviceConnectionRegistry;
};
-TEST_F(DBusServiceRegistryTest, CanBeConstructed) {
- std::shared_ptr<CommonAPI::DBus::DBusConnection> dbusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
- CommonAPI::DBus::DBusServiceRegistry* registry = new CommonAPI::DBus::DBusServiceRegistry(dbusConnection);
- ASSERT_TRUE(registry != NULL);
- delete registry;
+TEST_F(DBusServiceRegistryTest, DBusConnectionHasRegistry) {
+ clientDBusConnection->connect();
+
+ auto dbusServiceRegistry = clientDBusConnection->getDBusServiceRegistry();
+ EXPECT_FALSE(!dbusServiceRegistry);
}
+TEST_F(DBusServiceRegistryTest, SubscribeBeforeConnectWorks) {
+ TestDBusServiceListener testDBusServiceListener("local:Interface1:predefined.Instance1", clientDBusConnection);
+ testDBusServiceListener.subscribe();
+
+ ASSERT_TRUE(clientDBusConnection->connect());
+
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::NOT_AVAILABLE));
+ usleep(300 * 1000);
+ EXPECT_EQ(testDBusServiceListener.availabilityStatusCount, 1);
+
+ ASSERT_TRUE(serviceDBusConnection->connect());
+ ASSERT_TRUE(serviceDBusConnection->requestServiceNameAndBlock(dbusServiceName));
+ auto testDBusStubAdapter = std::make_shared<TestDBusStubAdapter>(
+ serviceFactory,
+ "local:Interface1:predefined.Instance1",
+ "tests.Interface1",
+ dbusServiceName,
+ "/tests/predefined/Object1",
+ serviceDBusConnection);
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(testDBusStubAdapter);
+
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::AVAILABLE));
+ sleep(2);
+ EXPECT_LE(testDBusServiceListener.availabilityStatusCount, 3);
+ EXPECT_EQ(testDBusStubAdapter->introspectionCount, 1);
+
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(testDBusStubAdapter->getAddress());
+
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::NOT_AVAILABLE));
+ EXPECT_LE(testDBusServiceListener.availabilityStatusCount, 4);
+}
-TEST_F(DBusServiceRegistryTest, DBusConnectionHasRegistry) {
- auto dbusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
- dbusConnection->connect();
- auto serviceRegistry = dbusConnection->getDBusServiceRegistry();
- ASSERT_FALSE(!serviceRegistry);
+TEST_F(DBusServiceRegistryTest, SubscribeBeforeConnectWithServiceWorks) {
+ ASSERT_TRUE(serviceDBusConnection->connect());
+ auto testDBusStubAdapter = std::make_shared<TestDBusStubAdapter>(
+ serviceFactory,
+ "local:Interface1:predefined.Instance1",
+ "tests.Interface1",
+ dbusServiceName,
+ "/tests/predefined/Object1",
+ serviceDBusConnection);
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(testDBusStubAdapter);
+
+ TestDBusServiceListener testDBusServiceListener("local:Interface1:predefined.Instance1", clientDBusConnection);
+ testDBusServiceListener.subscribe();
+ ASSERT_TRUE(clientDBusConnection->connect());
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::AVAILABLE));
+ usleep(300 * 1000);
+ EXPECT_EQ(testDBusServiceListener.availabilityStatusCount, 1);
+ EXPECT_EQ(testDBusStubAdapter->introspectionCount, 1);
+
+ serviceDBusConnection->disconnect();
+
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::NOT_AVAILABLE));
+ usleep(300 * 1000);
+ EXPECT_EQ(testDBusServiceListener.availabilityStatusCount, 2);
+
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(testDBusStubAdapter->getAddress());
+}
+
+TEST_F(DBusServiceRegistryTest, SubscribeAfterConnectWithServiceWorks) {
+ ASSERT_TRUE(serviceDBusConnection->connect());
+ auto testDBusStubAdapter = std::make_shared<TestDBusStubAdapter>(
+ serviceFactory,
+ "local:Interface1:predefined.Instance1",
+ "tests.Interface1",
+ dbusServiceName,
+ "/tests/predefined/Object1",
+ serviceDBusConnection);
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(testDBusStubAdapter);
+
+ ASSERT_TRUE(clientDBusConnection->connect());
+ TestDBusServiceListener testDBusServiceListener("local:Interface1:predefined.Instance1", clientDBusConnection);
+ EXPECT_EQ(testDBusStubAdapter->introspectionCount, 0);
+
+ testDBusServiceListener.subscribe();
+
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::AVAILABLE));
+ EXPECT_EQ(testDBusServiceListener.availabilityStatusCount, 1);
+ EXPECT_EQ(testDBusStubAdapter->introspectionCount, 1);
+
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterService(testDBusStubAdapter->getAddress());
+
+ EXPECT_TRUE(waitForAvailabilityStatusChanged(
+ testDBusServiceListener,
+ CommonAPI::AvailabilityStatus::NOT_AVAILABLE));
+ EXPECT_EQ(testDBusServiceListener.availabilityStatusCount, 2);
+ EXPECT_EQ(testDBusStubAdapter->introspectionCount, 1);
}
TEST_F(DBusServiceRegistryTest, DBusAddressTranslatorPredefinedWorks) {
@@ -106,26 +326,26 @@ TEST_F(DBusServiceRegistryTest, DBusAddressTranslatorPredefinedWorks) {
CommonAPI::DBus::DBusAddressTranslator::getInstance().getPredefinedInstances(dbusServiceName, loadedPredefinedInstances);
- ASSERT_EQ(loadedPredefinedInstances.size(), predefinedInstancesMap.size());
+ EXPECT_EQ(loadedPredefinedInstances.size(), predefinedInstancesMap.size());
for (auto& dbusServiceAddress : loadedPredefinedInstances) {
const std::string& loadedDBusServiceName = std::get<0>(dbusServiceAddress);
const std::string& loadedDBusObjectPath = std::get<1>(dbusServiceAddress);
const std::string& loadedDBusInterfaceName = std::get<2>(dbusServiceAddress);
- ASSERT_EQ(loadedDBusServiceName, dbusServiceName);
+ EXPECT_EQ(loadedDBusServiceName, dbusServiceName);
auto predefinedInstanceIterator = predefinedInstancesMap.find({ loadedDBusInterfaceName, loadedDBusObjectPath });
const bool predefinedInstanceFound = (predefinedInstanceIterator != predefinedInstancesMap.end());
- ASSERT_TRUE(predefinedInstanceFound);
+ EXPECT_TRUE(predefinedInstanceFound);
const std::string& commonApiAddress = predefinedInstanceIterator->second;
const std::string& predefinedDBusInterfaceName = predefinedInstanceIterator->first.first;
const std::string& predefinedDBusObjectPath = predefinedInstanceIterator->first.second;
- ASSERT_EQ(loadedDBusInterfaceName, predefinedDBusInterfaceName);
- ASSERT_EQ(loadedDBusObjectPath, predefinedDBusObjectPath);
+ EXPECT_EQ(loadedDBusInterfaceName, predefinedDBusInterfaceName);
+ EXPECT_EQ(loadedDBusObjectPath, predefinedDBusObjectPath);
std::string foundDBusInterfaceName;
std::string foundDBusServiceName;
@@ -137,86 +357,86 @@ TEST_F(DBusServiceRegistryTest, DBusAddressTranslatorPredefinedWorks) {
foundDBusServiceName,
foundDBusObjectPath);
- ASSERT_EQ(foundDBusInterfaceName, predefinedDBusInterfaceName);
- ASSERT_EQ(foundDBusServiceName, dbusServiceName);
- ASSERT_EQ(foundDBusObjectPath, predefinedDBusObjectPath);
+ EXPECT_EQ(foundDBusInterfaceName, predefinedDBusInterfaceName);
+ EXPECT_EQ(foundDBusServiceName, dbusServiceName);
+ EXPECT_EQ(foundDBusObjectPath, predefinedDBusObjectPath);
}
}
TEST_F(DBusServiceRegistryTest, PredefinedInstances) {
- auto stubDBusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
-
- ASSERT_TRUE(stubDBusConnection->connect());
- ASSERT_TRUE(stubDBusConnection->requestServiceNameAndBlock(dbusServiceName));
-
- auto proxyDBusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
- auto dbusServiceRegistry = proxyDBusConnection->getDBusServiceRegistry();
- std::unordered_map<std::string, std::promise<CommonAPI::AvailabilityStatus> > instanceStatusPromises;
- std::unordered_map<std::string, CommonAPI::DBus::DBusServiceRegistry::Subscription> instanceSubscriptions;
-
- for (auto& predefinedInstance : predefinedInstancesMap) {
- const std::string& commonApiAddress = predefinedInstance.second;
-
- instanceSubscriptions[commonApiAddress] = dbusServiceRegistry->subscribeAvailabilityListener(
- commonApiAddress,
- [&] (const CommonAPI::AvailabilityStatus& availabilityStatus) -> CommonAPI::SubscriptionStatus {
- instanceStatusPromises[commonApiAddress].set_value(availabilityStatus);
- return CommonAPI::SubscriptionStatus::RETAIN;
- });
- }
-
- ASSERT_TRUE(proxyDBusConnection->connect());
-
- for (auto& predefinedInstance : predefinedInstancesMap) {
- const std::string& dbusInterfaceName = predefinedInstance.first.first;
- const std::string& dbusObjectPath = predefinedInstance.first.second;
- const std::string& commonApiAddress = predefinedInstance.second;
-
- auto instanceStatusFuture = instanceStatusPromises[commonApiAddress].get_future();
- auto instanceStatusFutureStatus = instanceStatusFuture.wait_for(std::chrono::milliseconds(2000));
- const bool instanceReady = CommonAPI::DBus::checkReady(instanceStatusFutureStatus);
-
- ASSERT_TRUE(instanceReady);
-
- std::promise<CommonAPI::AvailabilityStatus> postInstanceStatusPromise;
- auto postInstanceSubscription = dbusServiceRegistry->subscribeAvailabilityListener(
- commonApiAddress,
- [&] (const CommonAPI::AvailabilityStatus& availabilityStatus) -> CommonAPI::SubscriptionStatus {
- postInstanceStatusPromise.set_value(availabilityStatus);
- return CommonAPI::SubscriptionStatus::RETAIN;
- });
-
- auto postInstanceStatusFuture = postInstanceStatusPromise.get_future();
- auto postInstanceStatusFutureStatus = postInstanceStatusFuture.wait_for(std::chrono::milliseconds(2000));
- const bool postInstanceReady = CommonAPI::DBus::checkReady(postInstanceStatusFutureStatus);
-
- ASSERT_TRUE(postInstanceReady);
-
- dbusServiceRegistry->unsubscribeAvailabilityListener(commonApiAddress, postInstanceSubscription);
- dbusServiceRegistry->unsubscribeAvailabilityListener(commonApiAddress, instanceSubscriptions[commonApiAddress]);
-
-
- bool isInstanceAlive = dbusServiceRegistry->isServiceInstanceAlive(dbusInterfaceName, dbusServiceName, dbusObjectPath);
- for (int i = 0; !isInstanceAlive && i < 100; i++) {
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- isInstanceAlive = dbusServiceRegistry->isServiceInstanceAlive(dbusInterfaceName, dbusServiceName, dbusObjectPath);
- }
-
- ASSERT_TRUE(isInstanceAlive);
-
-
- std::vector<std::string> availableDBusServiceInstances = dbusServiceRegistry->getAvailableServiceInstances(dbusInterfaceName);
- bool availableInstanceFound = false;
-
- for (auto& availableInstance : availableDBusServiceInstances) {
- if (availableInstance == commonApiAddress) {
- availableInstanceFound = true;
- break;
- }
- }
-
- ASSERT_TRUE(availableInstanceFound);
- }
+// auto stubDBusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
+//
+// ASSERT_TRUE(stubDBusConnection->connect());
+// ASSERT_TRUE(stubDBusConnection->requestServiceNameAndBlock(dbusServiceName));
+//
+// auto proxyDBusConnection = CommonAPI::DBus::DBusConnection::getSessionBus();
+// auto dbusServiceRegistry = proxyDBusConnection->getDBusServiceRegistry();
+// std::unordered_map<std::string, std::promise<CommonAPI::AvailabilityStatus> > instanceStatusPromises;
+// std::unordered_map<std::string, CommonAPI::DBus::DBusServiceRegistry::Subscription> instanceSubscriptions;
+//
+// for (auto& predefinedInstance : predefinedInstancesMap) {
+// const std::string& commonApiAddress = predefinedInstance.second;
+//
+// instanceSubscriptions[commonApiAddress] = dbusServiceRegistry->subscribeAvailabilityListener(
+// commonApiAddress,
+// [&] (const CommonAPI::AvailabilityStatus& availabilityStatus) -> CommonAPI::SubscriptionStatus {
+// instanceStatusPromises[commonApiAddress].set_value(availabilityStatus);
+// return CommonAPI::SubscriptionStatus::RETAIN;
+// });
+// }
+//
+// ASSERT_TRUE(proxyDBusConnection->connect());
+//
+// for (auto& predefinedInstance : predefinedInstancesMap) {
+// const std::string& dbusInterfaceName = predefinedInstance.first.first;
+// const std::string& dbusObjectPath = predefinedInstance.first.second;
+// const std::string& commonApiAddress = predefinedInstance.second;
+//
+// auto instanceStatusFuture = instanceStatusPromises[commonApiAddress].get_future();
+// auto instanceStatusFutureStatus = instanceStatusFuture.wait_for(std::chrono::milliseconds(2000));
+// const bool instanceReady = CommonAPI::DBus::checkReady(instanceStatusFutureStatus);
+//
+// ASSERT_TRUE(instanceReady);
+//
+// std::promise<CommonAPI::AvailabilityStatus> postInstanceStatusPromise;
+// auto postInstanceSubscription = dbusServiceRegistry->subscribeAvailabilityListener(
+// commonApiAddress,
+// [&] (const CommonAPI::AvailabilityStatus& availabilityStatus) -> CommonAPI::SubscriptionStatus {
+// postInstanceStatusPromise.set_value(availabilityStatus);
+// return CommonAPI::SubscriptionStatus::RETAIN;
+// });
+//
+// auto postInstanceStatusFuture = postInstanceStatusPromise.get_future();
+// auto postInstanceStatusFutureStatus = postInstanceStatusFuture.wait_for(std::chrono::milliseconds(2000));
+// const bool postInstanceReady = CommonAPI::DBus::checkReady(postInstanceStatusFutureStatus);
+//
+// ASSERT_TRUE(postInstanceReady);
+//
+// dbusServiceRegistry->unsubscribeAvailabilityListener(commonApiAddress, postInstanceSubscription);
+// dbusServiceRegistry->unsubscribeAvailabilityListener(commonApiAddress, instanceSubscriptions[commonApiAddress]);
+//
+//
+// bool isInstanceAlive = dbusServiceRegistry->isServiceInstanceAlive(dbusInterfaceName, dbusServiceName, dbusObjectPath);
+// for (int i = 0; !isInstanceAlive && i < 100; i++) {
+// std::this_thread::sleep_for(std::chrono::milliseconds(10));
+// isInstanceAlive = dbusServiceRegistry->isServiceInstanceAlive(dbusInterfaceName, dbusServiceName, dbusObjectPath);
+// }
+//
+// ASSERT_TRUE(isInstanceAlive);
+//
+//
+// std::vector<std::string> availableDBusServiceInstances = dbusServiceRegistry->getAvailableServiceInstances(dbusInterfaceName);
+// bool availableInstanceFound = false;
+//
+// for (auto& availableInstance : availableDBusServiceInstances) {
+// if (availableInstance == commonApiAddress) {
+// availableInstanceFound = true;
+// break;
+// }
+// }
+//
+// ASSERT_TRUE(availableInstanceFound);
+// }
}
@@ -225,6 +445,9 @@ const char serviceName_[] = "test.service.name";
const char nonexistingServiceAddress_[] = "local:nonexisting.service.name:nonexisting.instance.name";
const char nonexistingServiceName_[] = "nonexisting.service.name";
+
+
+
class DBusServiceDiscoveryTestWithPredefinedRemote: public ::testing::Test {
protected:
virtual void SetUp() {
@@ -249,15 +472,14 @@ class DBusServiceDiscoveryTestWithPredefinedRemote: public ::testing::Test {
std::shared_ptr<CommonAPI::ServicePublisher> servicePublisher_;
};
-
TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, RecognizesInstanceOfExistingServiceAsAlive) {
bool result = clientFactory_->isServiceInstanceAlive(serviceAddress_);
- ASSERT_TRUE(result);
+ EXPECT_TRUE(result);
}
TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, RecognizesInstanceOfNonexistingServiceAsDead) {
bool result = clientFactory_->isServiceInstanceAlive(nonexistingServiceAddress_);
- ASSERT_FALSE(result);
+ EXPECT_FALSE(result);
}
@@ -274,7 +496,7 @@ TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, RecognizesInstanceOfExistin
},
serviceAddress_);
- ASSERT_TRUE(futureResult.get());
+ EXPECT_TRUE(futureResult.get());
}
@@ -291,12 +513,12 @@ TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, RecognizesInstanceOfNonexis
},
nonexistingServiceAddress_);
- ASSERT_FALSE(futureResult.get());
+ EXPECT_FALSE(futureResult.get());
}
TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, FindsInstancesOfExistingTestService) {
- ASSERT_EQ(1, clientFactory_->getAvailableServiceInstances(serviceName_).size());
+ EXPECT_EQ(1, clientFactory_->getAvailableServiceInstances(serviceName_).size());
}
@@ -316,13 +538,13 @@ TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, FindsInstancesOfExistingTes
std::vector<std::string> result = futureResult.get();
- ASSERT_EQ(1, result.size());
+ EXPECT_EQ(1, result.size());
}
TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, FindsNoInstancesOfNonexistingTestService) {
std::vector<std::string> result = clientFactory_->getAvailableServiceInstances(nonexistingServiceName_);
- ASSERT_EQ(0, result.size());
+ EXPECT_EQ(0, result.size());
}
@@ -339,9 +561,50 @@ TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, FindsNoInstancesOfNonexisti
},
nonexistingServiceName_);
- ASSERT_EQ(0, futureResult.get().size());
+ EXPECT_EQ(0, futureResult.get().size());
}
+TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, ServiceRegistryUsesCacheForResolvingOneService) {
+ std::chrono::system_clock::time_point startTimeWithColdCache = std::chrono::system_clock::now();
+ ASSERT_TRUE(clientFactory_->isServiceInstanceAlive(serviceAddress_));
+ std::chrono::system_clock::time_point endTimeWithColdCache = std::chrono::system_clock::now();
+
+ long durationWithColdCache = std::chrono::duration_cast<std::chrono::microseconds>(endTimeWithColdCache - startTimeWithColdCache).count();
+
+ std::chrono::system_clock::time_point startTimeWithHotCache = std::chrono::system_clock::now();
+ ASSERT_TRUE(clientFactory_->isServiceInstanceAlive(serviceAddress_));
+ std::chrono::system_clock::time_point endTimeWithHotCache = std::chrono::system_clock::now();
+
+ long durationWithHotCache = std::chrono::duration_cast<std::chrono::microseconds>(endTimeWithHotCache - startTimeWithHotCache).count();
+
+ double speedRatio = durationWithColdCache / durationWithHotCache;
+
+ EXPECT_GE(speedRatio, 100);
+}
+
+
+TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, DISABLED_ServiceRegistryUsesCacheForResolvingWholeBus) {
+ std::chrono::system_clock::time_point startTimeWithColdCache = std::chrono::system_clock::now();
+ ASSERT_EQ(1, clientFactory_->getAvailableServiceInstances(serviceName_).size());
+ std::chrono::system_clock::time_point endTimeWithColdCache = std::chrono::system_clock::now();
+
+ long durationWithColdCache = std::chrono::duration_cast<std::chrono::microseconds>(endTimeWithColdCache - startTimeWithColdCache).count();
+
+ std::chrono::system_clock::time_point startTimeWithHotCache = std::chrono::system_clock::now();
+ ASSERT_EQ(1, clientFactory_->getAvailableServiceInstances(serviceName_).size());
+ std::chrono::system_clock::time_point endTimeWithHotCache = std::chrono::system_clock::now();
+
+ long durationWithHotCache = std::chrono::duration_cast<std::chrono::microseconds>(endTimeWithHotCache - startTimeWithHotCache).count();
+
+ double speedRatio = durationWithColdCache / durationWithHotCache;
+
+ std::cout << "cold " << durationWithColdCache << "\n";
+ std::cout << "hot " << durationWithHotCache << "\n";
+
+ EXPECT_GE(speedRatio, 100);
+}
+
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/src/test/DBusTestUtils.h b/src/test/DBusTestUtils.h
index 476840b..4c1d3af 100644
--- a/src/test/DBusTestUtils.h
+++ b/src/test/DBusTestUtils.h
@@ -35,17 +35,17 @@ inline void printLibdbusMessage(DBusMessage* libdbusMessage) {
printLibdbusMessage(libdbusMessage, 0, dbus_message_get_body_length(libdbusMessage));
}
-inline std::string toString(CommonAPI::DBus::DBusServiceRegistry::DBusServiceState state) {
- switch(state) {
- case CommonAPI::DBus::DBusServiceRegistry::DBusServiceState::AVAILABLE:
+inline std::string toString(CommonAPI::DBus::DBusServiceRegistry::DBusRecordState dbusRecordState) {
+ switch(dbusRecordState) {
+ case CommonAPI::DBus::DBusServiceRegistry::DBusRecordState::AVAILABLE:
return "AVAILABLE";
- case CommonAPI::DBus::DBusServiceRegistry::DBusServiceState::NOT_AVAILABLE:
+ case CommonAPI::DBus::DBusServiceRegistry::DBusRecordState::NOT_AVAILABLE:
return "NOT_AVAILABLE";
- case CommonAPI::DBus::DBusServiceRegistry::DBusServiceState::RESOLVED:
+ case CommonAPI::DBus::DBusServiceRegistry::DBusRecordState::RESOLVED:
return "RESOLVED";
- case CommonAPI::DBus::DBusServiceRegistry::DBusServiceState::RESOLVING:
+ case CommonAPI::DBus::DBusServiceRegistry::DBusRecordState::RESOLVING:
return "RESOLVING";
- case CommonAPI::DBus::DBusServiceRegistry::DBusServiceState::UNKNOWN:
+ case CommonAPI::DBus::DBusServiceRegistry::DBusRecordState::UNKNOWN:
return "UNKNOWN";
}
}
diff --git a/src/test/commonapi/tests/DerivedTypeCollection.cpp b/src/test/commonapi/tests/DerivedTypeCollection.cpp
index 5b5ca7b..865ebae 100644
--- a/src/test/commonapi/tests/DerivedTypeCollection.cpp
+++ b/src/test/commonapi/tests/DerivedTypeCollection.cpp
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#include "DerivedTypeCollection.h"
@@ -12,6 +14,8 @@ namespace commonapi {
namespace tests {
namespace DerivedTypeCollection {
+
+
TestStructExtended::TestStructExtended(const PredefinedTypeCollection::TestString& testStringValue, const uint16_t& uintValueValue, const TestEnumExtended2& testEnumExtended2Value):
TestStruct(testStringValue, uintValueValue),
testEnumExtended2(testEnumExtended2Value)
@@ -38,6 +42,14 @@ void TestStructExtended::writeToOutputStream(CommonAPI::OutputStream& outputStre
TestStruct::writeToOutputStream(outputStream);
outputStream << testEnumExtended2;
}
+
+
+
+
+
+
+
+
TestStruct::TestStruct(const PredefinedTypeCollection::TestString& testStringValue, const uint16_t& uintValueValue):
testString(testStringValue),
uintValue(uintValueValue)
diff --git a/src/test/commonapi/tests/DerivedTypeCollection.h b/src/test/commonapi/tests/DerivedTypeCollection.h
index 0ca1d79..52687f6 100644
--- a/src/test/commonapi/tests/DerivedTypeCollection.h
+++ b/src/test/commonapi/tests/DerivedTypeCollection.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Derived_Type_Collection_H_
#define COMMONAPI_TESTS_Derived_Type_Collection_H_
@@ -31,8 +33,14 @@ namespace tests {
namespace DerivedTypeCollection {
struct TestStruct: CommonAPI::SerializableStruct {
- PredefinedTypeCollection::TestString testString;
- uint16_t uintValue;
+ /**
+ * the name of the property
+ */
+ PredefinedTypeCollection::TestString testString;
+ /**
+ * the actual value
+ */
+ uint16_t uintValue;
TestStruct() = default;
TestStruct(const PredefinedTypeCollection::TestString& testString, const uint16_t& uintValue);
@@ -46,33 +54,46 @@ namespace DerivedTypeCollection {
typeOutputStream.writeUInt16Type();
}
};
-
typedef std::vector<TestStruct> TestArrayTestStruct;
-
typedef std::unordered_map<uint32_t, TestArrayTestStruct> TestMap;
-
+ /**
+ * Common errors.
+ */
enum class TestEnum: int32_t {
+ /**
+ * default
+ */
E_UNKNOWN = 0x0,
+ /**
+ * no error - positive reply
+ */
E_OK = 0x1,
+ /**
+ * value out of range
+ */
E_OUT_OF_RANGE = 0x2,
+ /**
+ * not used
+ */
E_NOT_USED = 0x3
};
// Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
struct TestEnumComparator;
-
enum class TestEnumExtended: int32_t {
E_UNKNOWN = TestEnum::E_UNKNOWN,
E_OK = TestEnum::E_OK,
E_OUT_OF_RANGE = TestEnum::E_OUT_OF_RANGE,
E_NOT_USED = TestEnum::E_NOT_USED
,
+ /**
+ * new error
+ */
E_NEW = 0x4
};
// Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
struct TestEnumExtendedComparator;
-
enum class TestEnumExtended2: int32_t {
E_UNKNOWN = TestEnum::E_UNKNOWN,
E_OK = TestEnum::E_OK,
@@ -81,14 +102,16 @@ namespace DerivedTypeCollection {
E_NEW = TestEnumExtended::E_NEW
,
+ /**
+ * new error
+ */
E_NEW2 = 0x5
};
// Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
struct TestEnumExtended2Comparator;
-
struct TestStructExtended: TestStruct {
- TestEnumExtended2 testEnumExtended2;
+ TestEnumExtended2 testEnumExtended2;
TestStructExtended() = default;
TestStructExtended(const PredefinedTypeCollection::TestString& testString, const uint16_t& uintValue, const TestEnumExtended2& testEnumExtended2);
@@ -102,10 +125,11 @@ namespace DerivedTypeCollection {
typeOutputStream.writeInt32Type();
}
};
-
typedef std::unordered_map<TestEnum, std::string> TestEnumMap;
-
enum class TestEnumMissingValue: int32_t {
+ /**
+ * default
+ */
E1 = 0xa,
E2,
E3 = 2
@@ -113,9 +137,7 @@ namespace DerivedTypeCollection {
// Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
struct TestEnumMissingValueComparator;
-
typedef std::vector<uint64_t> TestArrayUInt64;
-
bool operator==(const TestStructExtended& lhs, const TestStructExtended& rhs);
inline bool operator!=(const TestStructExtended& lhs, const TestStructExtended& rhs) {
diff --git a/src/test/commonapi/tests/PredefinedTypeCollection.h b/src/test/commonapi/tests/PredefinedTypeCollection.h
index 3cf55ff..5f534b0 100644
--- a/src/test/commonapi/tests/PredefinedTypeCollection.h
+++ b/src/test/commonapi/tests/PredefinedTypeCollection.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Predefined_Type_Collection_H_
#define COMMONAPI_TESTS_Predefined_Type_Collection_H_
@@ -26,31 +28,18 @@ namespace tests {
namespace PredefinedTypeCollection {
typedef uint8_t TestUInt8;
-
typedef uint16_t TestUInt16;
-
typedef uint32_t TestUInt32;
-
typedef uint64_t TestUInt64;
-
typedef int8_t TestInt8;
-
typedef int16_t TestInt16;
-
typedef int32_t TestInt32;
-
typedef int64_t TestInt64;
-
typedef bool TestBoolean;
-
typedef CommonAPI::ByteBuffer TestByteBuffer;
-
typedef double TestDouble;
-
typedef float TestFloat;
-
typedef std::string TestString;
-
diff --git a/src/test/commonapi/tests/TestInterface.h b/src/test/commonapi/tests/TestInterface.h
index c38b541..5099bd0 100644
--- a/src/test/commonapi/tests/TestInterface.h
+++ b/src/test/commonapi/tests/TestInterface.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_H_
#define COMMONAPI_TESTS_Test_Interface_H_
diff --git a/src/test/commonapi/tests/TestInterfaceDBusProxy.cpp b/src/test/commonapi/tests/TestInterfaceDBusProxy.cpp
index e20db65..4dcaed5 100644
--- a/src/test/commonapi/tests/TestInterfaceDBusProxy.cpp
+++ b/src/test/commonapi/tests/TestInterfaceDBusProxy.cpp
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#include "TestInterfaceDBusProxy.h"
@@ -11,12 +13,13 @@ namespace commonapi {
namespace tests {
std::shared_ptr<CommonAPI::DBus::DBusProxy> createTestInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
const std::string& objectPath,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection) {
- return std::make_shared<TestInterfaceDBusProxy>(commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
+ return std::make_shared<TestInterfaceDBusProxy>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
}
__attribute__((constructor)) void registerTestInterfaceDBusProxy(void) {
@@ -25,20 +28,21 @@ __attribute__((constructor)) void registerTestInterfaceDBusProxy(void) {
}
TestInterfaceDBusProxy::TestInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
const std::string& objectPath,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection):
- CommonAPI::DBus::DBusProxy(commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
-, testPredefinedTypeAttribute_(*this, "onTestPredefinedTypeAttributeAttributeChanged", "setTestPredefinedTypeAttributeAttribute", "u", "getTestPredefinedTypeAttributeAttribute"),
- testDerivedStructAttribute_(*this, "onTestDerivedStructAttributeAttributeChanged", "setTestDerivedStructAttributeAttribute", "(sqi)", "getTestDerivedStructAttributeAttribute"),
- testDerivedArrayAttribute_(*this, "onTestDerivedArrayAttributeAttributeChanged", "setTestDerivedArrayAttributeAttribute", "at", "getTestDerivedArrayAttributeAttribute")
+ CommonAPI::DBus::DBusProxy(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
+,testPredefinedTypeAttribute_(*this, "onTestPredefinedTypeAttributeAttributeChanged", "setTestPredefinedTypeAttributeAttribute", "u", "getTestPredefinedTypeAttributeAttribute"),
+testDerivedStructAttribute_(*this, "onTestDerivedStructAttributeAttributeChanged", "setTestDerivedStructAttributeAttribute", "(sqi)", "getTestDerivedStructAttributeAttribute"),
+testDerivedArrayAttribute_(*this, "onTestDerivedArrayAttributeAttributeChanged", "setTestDerivedArrayAttributeAttribute", "at", "getTestDerivedArrayAttributeAttribute")
, testPredefinedTypeBroadcast_(*this, "TestPredefinedTypeBroadcast", "us"),
- testSelectiveBroadcastSelective_(*this, "TestSelectiveBroadcast", ""),
- testBroadcastWithOutArgsSelective_(*this, "TestBroadcastWithOutArgs", "us")
- {
-}
+ testSelectiveBroadcastSelective_(*this, "TestSelectiveBroadcast", ""),
+ testBroadcastWithOutArgsSelective_(*this, "TestBroadcastWithOutArgs", "us")
+ {
+ }
TestInterfaceDBusProxy::TestPredefinedTypeAttributeAttribute& TestInterfaceDBusProxy::getTestPredefinedTypeAttributeAttribute() {
return testPredefinedTypeAttribute_;
@@ -155,6 +159,7 @@ std::future<CommonAPI::CallStatus> TestInterfaceDBusProxy::testDerivedTypeMethod
}
+
void TestInterfaceDBusProxy::getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const {
ownVersionMajor = 1;
ownVersionMinor = 0;
diff --git a/src/test/commonapi/tests/TestInterfaceDBusProxy.h b/src/test/commonapi/tests/TestInterfaceDBusProxy.h
index 86d8fb3..3974b09 100644
--- a/src/test/commonapi/tests/TestInterfaceDBusProxy.h
+++ b/src/test/commonapi/tests/TestInterfaceDBusProxy.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_DBUS_PROXY_H_
#define COMMONAPI_TESTS_Test_Interface_DBUS_PROXY_H_
@@ -31,6 +33,7 @@ namespace tests {
class TestInterfaceDBusProxy: virtual public TestInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy {
public:
TestInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
@@ -57,19 +60,23 @@ class TestInterfaceDBusProxy: virtual public TestInterfaceProxyBase, virtual pub
virtual std::future<CommonAPI::CallStatus> testVoidDerivedTypeMethodAsync(const DerivedTypeCollection::TestEnumExtended2& testEnumExtended2Value, const DerivedTypeCollection::TestMap& testMapValue, TestVoidDerivedTypeMethodAsyncCallback callback);
virtual void testDerivedTypeMethod(const DerivedTypeCollection::TestEnumExtended2& testEnumExtended2InValue, const DerivedTypeCollection::TestMap& testMapInValue, CommonAPI::CallStatus& callStatus, DerivedTypeCollection::TestEnumExtended2& testEnumExtended2OutValue, DerivedTypeCollection::TestMap& testMapOutValue);
virtual std::future<CommonAPI::CallStatus> testDerivedTypeMethodAsync(const DerivedTypeCollection::TestEnumExtended2& testEnumExtended2InValue, const DerivedTypeCollection::TestMap& testMapInValue, TestDerivedTypeMethodAsyncCallback callback);
+
virtual void getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const;
private:
- CommonAPI::DBus::DBusObservableAttribute<CommonAPI::DBus::DBusAttribute<TestPredefinedTypeAttributeAttribute>> testPredefinedTypeAttribute_;
- CommonAPI::DBus::DBusObservableAttribute<CommonAPI::DBus::DBusAttribute<TestDerivedStructAttributeAttribute>> testDerivedStructAttribute_;
- CommonAPI::DBus::DBusObservableAttribute<CommonAPI::DBus::DBusAttribute<TestDerivedArrayAttributeAttribute>> testDerivedArrayAttribute_;
+ CommonAPI::DBus::DBusObservableAttribute<CommonAPI::DBus::DBusAttribute<TestPredefinedTypeAttributeAttribute>> testPredefinedTypeAttribute_;
+ CommonAPI::DBus::DBusObservableAttribute<CommonAPI::DBus::DBusAttribute<TestDerivedStructAttributeAttribute>> testDerivedStructAttribute_;
+ CommonAPI::DBus::DBusObservableAttribute<CommonAPI::DBus::DBusAttribute<TestDerivedArrayAttributeAttribute>> testDerivedArrayAttribute_;
CommonAPI::DBus::DBusEvent<TestPredefinedTypeBroadcastEvent> testPredefinedTypeBroadcast_;
CommonAPI::DBus::DBusSelectiveEvent<TestSelectiveBroadcastSelectiveEvent> testSelectiveBroadcastSelective_;
CommonAPI::DBus::DBusSelectiveEvent<TestBroadcastWithOutArgsSelectiveEvent> testBroadcastWithOutArgsSelective_;
+
};
+
+
} // namespace tests
} // namespace commonapi
diff --git a/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.cpp b/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.cpp
index ffe1005..74dedd8 100644
--- a/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.cpp
+++ b/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.cpp
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#include "TestInterfaceDBusStubAdapter.h"
#include <commonapi/tests/TestInterface.h>
@@ -12,13 +14,14 @@ namespace commonapi {
namespace tests {
std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> createTestInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
const std::string& objectPath,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection,
const std::shared_ptr<CommonAPI::StubBase>& stubBase) {
- return std::make_shared<TestInterfaceDBusStubAdapter>(commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase);
+ return std::make_shared<TestInterfaceDBusStubAdapter>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase);
}
__attribute__((constructor)) void registerTestInterfaceDBusStubAdapter(void) {
@@ -27,13 +30,28 @@ __attribute__((constructor)) void registerTestInterfaceDBusStubAdapter(void) {
}
TestInterfaceDBusStubAdapter::TestInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
const std::string& dbusObjectPath,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
const std::shared_ptr<CommonAPI::StubBase>& stub):
- TestInterfaceDBusStubAdapterHelper(commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, dbusConnection, std::dynamic_pointer_cast<TestInterfaceStub>(stub)) {
+ TestInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath,
+ dbusConnection, std::dynamic_pointer_cast<TestInterfaceStub>(stub),
+ NULL)
+ {
+ subscribersForTestSelectiveBroadcastSelective_ = std::make_shared<CommonAPI::ClientIdList>();
+ subscribersForTestBroadcastWithOutArgsSelective_ = std::make_shared<CommonAPI::ClientIdList>();
+}
+
+TestInterfaceDBusStubAdapter::~TestInterfaceDBusStubAdapter() {
+ deactivateManagedInstances();
+ deinit();
+ stub_.reset();
+}
+
+void TestInterfaceDBusStubAdapter::deactivateManagedInstances() {
}
const char* TestInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const {
@@ -229,24 +247,29 @@ static CommonAPI::DBus::DBusMethodWithReplyAdapterDispatcher<
void TestInterfaceDBusStubAdapter::fireTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId) {
std::shared_ptr<CommonAPI::DBus::DBusClientId> dbusClientId = std::dynamic_pointer_cast<CommonAPI::DBus::DBusClientId, CommonAPI::ClientId>(clientId);
- if(dbusClientId != NULL)
+ if(dbusClientId)
{
- CommonAPI::DBus::DBusMessage dbusMethodCall = dbusClientId->createMessage(getObjectPath(), getInterfaceName(), "TestSelectiveBroadcast");
- getDBusConnection()->sendDBusMessage(dbusMethodCall);
+ CommonAPI::DBus::DBusStubSignalHelper<CommonAPI::DBus::DBusSerializableArguments<>>
+ ::sendSignal(
+ dbusClientId->getDBusId(),
+ *this,
+ "TestSelectiveBroadcast",
+ ""
+ );
}
}
-void TestInterfaceDBusStubAdapter::sendTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers) {
- const CommonAPI::ClientIdList* actualReceiverList;
+void TestInterfaceDBusStubAdapter::sendTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientIdList> receivers) {
+ std::shared_ptr<CommonAPI::ClientIdList> actualReceiverList;
actualReceiverList = receivers;
if(receivers == NULL)
- actualReceiverList = &subscribersForTestSelectiveBroadcastSelective_;
+ actualReceiverList = subscribersForTestSelectiveBroadcastSelective_;
for (auto clientIdIterator = actualReceiverList->cbegin();
clientIdIterator != actualReceiverList->cend();
clientIdIterator++) {
- if(receivers == NULL || subscribersForTestSelectiveBroadcastSelective_.find(*clientIdIterator) != subscribersForTestSelectiveBroadcastSelective_.end()) {
+ if(receivers == NULL || subscribersForTestSelectiveBroadcastSelective_->find(*clientIdIterator) != subscribersForTestSelectiveBroadcastSelective_->end()) {
fireTestSelectiveBroadcastSelective(*clientIdIterator);
}
}
@@ -255,7 +278,7 @@ void TestInterfaceDBusStubAdapter::sendTestSelectiveBroadcastSelective(const Com
void TestInterfaceDBusStubAdapter::subscribeForTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, bool& success) {
bool ok = stub_->onTestSelectiveBroadcastSelectiveSubscriptionRequested(clientId);
if (ok) {
- subscribersForTestSelectiveBroadcastSelective_.insert(clientId);
+ subscribersForTestSelectiveBroadcastSelective_->insert(clientId);
stub_->onTestSelectiveBroadcastSelectiveSubscriptionChanged(clientId, CommonAPI::SelectiveBroadcastSubscriptionEvent::SUBSCRIBED);
success = true;
} else {
@@ -265,12 +288,12 @@ void TestInterfaceDBusStubAdapter::subscribeForTestSelectiveBroadcastSelective(c
void TestInterfaceDBusStubAdapter::unsubscribeFromTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId) {
- subscribersForTestSelectiveBroadcastSelective_.erase(clientId);
+ subscribersForTestSelectiveBroadcastSelective_->erase(clientId);
stub_->onTestSelectiveBroadcastSelectiveSubscriptionChanged(clientId, CommonAPI::SelectiveBroadcastSubscriptionEvent::UNSUBSCRIBED);
}
-CommonAPI::ClientIdList* const TestInterfaceDBusStubAdapter::getSubscribersForTestSelectiveBroadcastSelective() {
- return &subscribersForTestSelectiveBroadcastSelective_;
+std::shared_ptr<CommonAPI::ClientIdList> const TestInterfaceDBusStubAdapter::getSubscribersForTestSelectiveBroadcastSelective() {
+ return subscribersForTestSelectiveBroadcastSelective_;
}
static CommonAPI::DBus::DBusMethodWithReplyAdapterDispatcher<
@@ -291,24 +314,30 @@ static CommonAPI::DBus::DBusMethodWithReplyAdapterDispatcher<
void TestInterfaceDBusStubAdapter::fireTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, const uint32_t& uint32Value, const std::string& stringValue) {
std::shared_ptr<CommonAPI::DBus::DBusClientId> dbusClientId = std::dynamic_pointer_cast<CommonAPI::DBus::DBusClientId, CommonAPI::ClientId>(clientId);
- if(dbusClientId != NULL)
+ if(dbusClientId)
{
- CommonAPI::DBus::DBusMessage dbusMethodCall = dbusClientId->createMessage(getObjectPath(), getInterfaceName(), "TestBroadcastWithOutArgs");
- getDBusConnection()->sendDBusMessage(dbusMethodCall);
+ CommonAPI::DBus::DBusStubSignalHelper<CommonAPI::DBus::DBusSerializableArguments<uint32_t, std::string>>
+ ::sendSignal(
+ dbusClientId->getDBusId(),
+ *this,
+ "TestBroadcastWithOutArgs",
+ "us",
+ uint32Value, stringValue
+ );
}
}
-void TestInterfaceDBusStubAdapter::sendTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const CommonAPI::ClientIdList* receivers) {
- const CommonAPI::ClientIdList* actualReceiverList;
+void TestInterfaceDBusStubAdapter::sendTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const std::shared_ptr<CommonAPI::ClientIdList> receivers) {
+ std::shared_ptr<CommonAPI::ClientIdList> actualReceiverList;
actualReceiverList = receivers;
if(receivers == NULL)
- actualReceiverList = &subscribersForTestBroadcastWithOutArgsSelective_;
+ actualReceiverList = subscribersForTestBroadcastWithOutArgsSelective_;
for (auto clientIdIterator = actualReceiverList->cbegin();
clientIdIterator != actualReceiverList->cend();
clientIdIterator++) {
- if(receivers == NULL || subscribersForTestBroadcastWithOutArgsSelective_.find(*clientIdIterator) != subscribersForTestBroadcastWithOutArgsSelective_.end()) {
+ if(receivers == NULL || subscribersForTestBroadcastWithOutArgsSelective_->find(*clientIdIterator) != subscribersForTestBroadcastWithOutArgsSelective_->end()) {
fireTestBroadcastWithOutArgsSelective(*clientIdIterator, uint32Value, stringValue);
}
}
@@ -317,7 +346,7 @@ void TestInterfaceDBusStubAdapter::sendTestBroadcastWithOutArgsSelective(const u
void TestInterfaceDBusStubAdapter::subscribeForTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, bool& success) {
bool ok = stub_->onTestBroadcastWithOutArgsSelectiveSubscriptionRequested(clientId);
if (ok) {
- subscribersForTestBroadcastWithOutArgsSelective_.insert(clientId);
+ subscribersForTestBroadcastWithOutArgsSelective_->insert(clientId);
stub_->onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(clientId, CommonAPI::SelectiveBroadcastSubscriptionEvent::SUBSCRIBED);
success = true;
} else {
@@ -327,12 +356,12 @@ void TestInterfaceDBusStubAdapter::subscribeForTestBroadcastWithOutArgsSelective
void TestInterfaceDBusStubAdapter::unsubscribeFromTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId) {
- subscribersForTestBroadcastWithOutArgsSelective_.erase(clientId);
+ subscribersForTestBroadcastWithOutArgsSelective_->erase(clientId);
stub_->onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(clientId, CommonAPI::SelectiveBroadcastSubscriptionEvent::UNSUBSCRIBED);
}
-CommonAPI::ClientIdList* const TestInterfaceDBusStubAdapter::getSubscribersForTestBroadcastWithOutArgsSelective() {
- return &subscribersForTestBroadcastWithOutArgsSelective_;
+std::shared_ptr<CommonAPI::ClientIdList> const TestInterfaceDBusStubAdapter::getSubscribersForTestBroadcastWithOutArgsSelective() {
+ return subscribersForTestBroadcastWithOutArgsSelective_;
}
@@ -359,5 +388,6 @@ const TestInterfaceDBusStubAdapter::StubDispatcherTable& TestInterfaceDBusStubAd
return stubDispatcherTable;
}
+
} // namespace tests
} // namespace commonapi
diff --git a/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.h b/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.h
index 9a9022d..d83081c 100644
--- a/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.h
+++ b/src/test/commonapi/tests/TestInterfaceDBusStubAdapter.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_DBUS_STUB_ADAPTER_H_
#define COMMONAPI_TESTS_Test_Interface_DBUS_STUB_ADAPTER_H_
@@ -15,7 +17,9 @@
#endif
#include <CommonAPI/DBus/DBusStubAdapterHelper.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
#undef COMMONAPI_INTERNAL_COMPILATION
@@ -27,6 +31,7 @@ typedef CommonAPI::DBus::DBusStubAdapterHelper<TestInterfaceStub> TestInterfaceD
class TestInterfaceDBusStubAdapter: public TestInterfaceStubAdapter, public TestInterfaceDBusStubAdapterHelper {
public:
TestInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& dbusInterfaceName,
const std::string& dbusBusName,
@@ -34,26 +39,33 @@ class TestInterfaceDBusStubAdapter: public TestInterfaceStubAdapter, public Test
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
const std::shared_ptr<CommonAPI::StubBase>& stub);
+ ~TestInterfaceDBusStubAdapter();
+
void fireTestPredefinedTypeAttributeAttributeChanged(const uint32_t& value);
void fireTestDerivedStructAttributeAttributeChanged(const DerivedTypeCollection::TestStructExtended& value);
void fireTestDerivedArrayAttributeAttributeChanged(const DerivedTypeCollection::TestArrayUInt64& value);
void fireTestPredefinedTypeBroadcastEvent(const uint32_t& uint32Value, const std::string& stringValue);
void fireTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId);
- void sendTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers = NULL);
+ void sendTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL);
void subscribeForTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, bool& success);
void unsubscribeFromTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId);
- CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective();
+ std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestSelectiveBroadcastSelective();
void fireTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, const uint32_t& uint32Value, const std::string& stringValue);
- void sendTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const CommonAPI::ClientIdList* receivers = NULL);
+ void sendTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL);
void subscribeForTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, bool& success);
void unsubscribeFromTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId);
- CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective();
+ std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestBroadcastWithOutArgsSelective();
+
const StubDispatcherTable& getStubDispatcherTable();
+
+ void deactivateManagedInstances();
protected:
virtual const char* getMethodsDBusIntrospectionXmlData() const;
+
+ private:
};
} // namespace tests
diff --git a/src/test/commonapi/tests/TestInterfaceProxy.h b/src/test/commonapi/tests/TestInterfaceProxy.h
index e206944..3aede95 100644
--- a/src/test/commonapi/tests/TestInterfaceProxy.h
+++ b/src/test/commonapi/tests/TestInterfaceProxy.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_PROXY_H_
#define COMMONAPI_TESTS_Test_Interface_PROXY_H_
@@ -28,28 +30,40 @@ class TestInterfaceProxy: virtual public TestInterface, virtual public TestInter
TestInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate);
~TestInterfaceProxy();
- /// Returns the wrapper class that provides access to the attribute TestPredefinedTypeAttribute.
+ /**
+ * Returns the wrapper class that provides access to the attribute TestPredefinedTypeAttribute.
+ */
virtual TestPredefinedTypeAttributeAttribute& getTestPredefinedTypeAttributeAttribute() {
return delegate_->getTestPredefinedTypeAttributeAttribute();
}
- /// Returns the wrapper class that provides access to the attribute TestDerivedStructAttribute.
+ /**
+ * Returns the wrapper class that provides access to the attribute TestDerivedStructAttribute.
+ */
virtual TestDerivedStructAttributeAttribute& getTestDerivedStructAttributeAttribute() {
return delegate_->getTestDerivedStructAttributeAttribute();
}
- /// Returns the wrapper class that provides access to the attribute TestDerivedArrayAttribute.
+ /**
+ * Returns the wrapper class that provides access to the attribute TestDerivedArrayAttribute.
+ */
virtual TestDerivedArrayAttributeAttribute& getTestDerivedArrayAttributeAttribute() {
return delegate_->getTestDerivedArrayAttributeAttribute();
}
- // Returns the wrapper class that provides access to the broadcast TestPredefinedTypeBroadcast.
+ /**
+ * Returns the wrapper class that provides access to the broadcast TestPredefinedTypeBroadcast.
+ */
virtual TestPredefinedTypeBroadcastEvent& getTestPredefinedTypeBroadcastEvent() {
return delegate_->getTestPredefinedTypeBroadcastEvent();
}
- // Returns the wrapper class that provides access to the broadcast TestSelectiveBroadcast.
+ /**
+ * Returns the wrapper class that provides access to the broadcast TestSelectiveBroadcast.
+ */
virtual TestSelectiveBroadcastSelectiveEvent& getTestSelectiveBroadcastSelectiveEvent() {
return delegate_->getTestSelectiveBroadcastSelectiveEvent();
}
- // Returns the wrapper class that provides access to the broadcast TestBroadcastWithOutArgs.
+ /**
+ * Returns the wrapper class that provides access to the broadcast TestBroadcastWithOutArgs.
+ */
virtual TestBroadcastWithOutArgsSelectiveEvent& getTestBroadcastWithOutArgsSelectiveEvent() {
return delegate_->getTestBroadcastWithOutArgsSelectiveEvent();
}
@@ -76,7 +90,7 @@ class TestInterfaceProxy: virtual public TestInterface, virtual public TestInter
/**
* Calls testVoidPredefinedTypeMethod with synchronous semantics.
*
- * All const parameters are input parameters to this method.
+ * All const parameters are input parameters to this method.
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
@@ -96,8 +110,8 @@ class TestInterfaceProxy: virtual public TestInterface, virtual public TestInter
/**
* Calls testPredefinedTypeMethod with synchronous semantics.
*
- * All const parameters are input parameters to this method.
- * All non-const parameters will be filled with the returned values.
+ * All const parameters are input parameters to this method.
+ * All non-const parameters will be filled with the returned values.
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
@@ -117,7 +131,7 @@ class TestInterfaceProxy: virtual public TestInterface, virtual public TestInter
/**
* Calls testVoidDerivedTypeMethod with synchronous semantics.
*
- * All const parameters are input parameters to this method.
+ * All const parameters are input parameters to this method.
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
@@ -137,8 +151,8 @@ class TestInterfaceProxy: virtual public TestInterface, virtual public TestInter
/**
* Calls testDerivedTypeMethod with synchronous semantics.
*
- * All const parameters are input parameters to this method.
- * All non-const parameters will be filled with the returned values.
+ * All const parameters are input parameters to this method.
+ * All non-const parameters will be filled with the returned values.
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
@@ -155,23 +169,39 @@ class TestInterfaceProxy: virtual public TestInterface, virtual public TestInter
* It will provide the same value for CallStatus as will be handed to the callback.
*/
virtual std::future<CommonAPI::CallStatus> testDerivedTypeMethodAsync(const DerivedTypeCollection::TestEnumExtended2& testEnumExtended2InValue, const DerivedTypeCollection::TestMap& testMapInValue, TestDerivedTypeMethodAsyncCallback callback);
+
- /// Returns the CommonAPI address of the remote partner this proxy communicates with.
+ /**
+ * Returns the CommonAPI address of the remote partner this proxy communicates with.
+ */
virtual std::string getAddress() const;
- /// Returns the domain of the remote partner this proxy communicates with.
+ /**
+ * Returns the domain of the remote partner this proxy communicates with.
+ */
virtual const std::string& getDomain() const;
- /// Returns the service ID of the remote partner this proxy communicates with.
+ /**
+ * Returns the service ID of the remote partner this proxy communicates with.
+ */
virtual const std::string& getServiceId() const;
- /// Returns the instance ID of the remote partner this proxy communicates with.
+ /**
+ * Returns the instance ID of the remote partner this proxy communicates with.
+ */
virtual const std::string& getInstanceId() const;
- /// Returns true if the remote partner for this proxy is available.
+ /**
+ * Returns true if the remote partner for this proxy is currently known to be available.
+ */
virtual bool isAvailable() const;
/**
+ * Returns true if the remote partner for this proxy is available.
+ */
+ virtual bool isAvailableBlocking() const;
+
+ /**
* Returns the wrapper class that is used to (de-)register for notifications about
* the availability of the remote partner of this proxy.
*/
@@ -332,6 +362,11 @@ bool TestInterfaceProxy<_AttributeExtensions...>::isAvailable() const {
}
template <typename ... _AttributeExtensions>
+bool TestInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const {
+ return delegate_->isAvailableBlocking();
+}
+
+template <typename ... _AttributeExtensions>
CommonAPI::ProxyStatusEvent& TestInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() {
return delegate_->getProxyStatusEvent();
}
@@ -341,6 +376,8 @@ CommonAPI::InterfaceVersionAttribute& TestInterfaceProxy<_AttributeExtensions...
return delegate_->getInterfaceVersionAttribute();
}
+
+
} // namespace tests
} // namespace commonapi
@@ -356,5 +393,4 @@ struct DefaultAttributeProxyFactoryHelper<commonapi::tests::TestInterfaceProxy,
};
}
-
#endif // COMMONAPI_TESTS_Test_Interface_PROXY_H_
diff --git a/src/test/commonapi/tests/TestInterfaceProxyBase.h b/src/test/commonapi/tests/TestInterfaceProxyBase.h
index 715df5c..619e27e 100644
--- a/src/test/commonapi/tests/TestInterfaceProxyBase.h
+++ b/src/test/commonapi/tests/TestInterfaceProxyBase.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_PROXY_BASE_H_
#define COMMONAPI_TESTS_Test_Interface_PROXY_BASE_H_
diff --git a/src/test/commonapi/tests/TestInterfaceStub.h b/src/test/commonapi/tests/TestInterfaceStub.h
index 59ef632..282dca5 100644
--- a/src/test/commonapi/tests/TestInterfaceStub.h
+++ b/src/test/commonapi/tests/TestInterfaceStub.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_STUB_H_
#define COMMONAPI_TESTS_Test_Interface_STUB_H_
@@ -57,26 +59,30 @@ class TestInterfaceStubAdapter: virtual public CommonAPI::StubAdapter, public Te
* Instead, the "fire<broadcastName>Event" methods of the stub should be used.
*/
virtual void fireTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId) = 0;
- virtual void sendTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers = NULL) = 0;
+ virtual void sendTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL) = 0;
virtual void subscribeForTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, bool& success) = 0;
virtual void unsubscribeFromTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientId> clientId) = 0;
- virtual CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective() = 0;
+ virtual std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestSelectiveBroadcastSelective() = 0;
/**
* Sends a selective broadcast event for TestBroadcastWithOutArgs. Should not be called directly.
* Instead, the "fire<broadcastName>Event" methods of the stub should be used.
*/
virtual void fireTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, const uint32_t& uint32Value, const std::string& stringValue) = 0;
- virtual void sendTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const CommonAPI::ClientIdList* receivers = NULL) = 0;
+ virtual void sendTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL) = 0;
virtual void subscribeForTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId, bool& success) = 0;
virtual void unsubscribeFromTestBroadcastWithOutArgsSelective(const std::shared_ptr<CommonAPI::ClientId> clientId) = 0;
- virtual CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective() = 0;
+ virtual std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestBroadcastWithOutArgsSelective() = 0;
+
+
+ virtual void deactivateManagedInstances() = 0;
+
protected:
/**
* Defines properties for storing the ClientIds of clients / proxies that have
* subscribed to the selective broadcasts
*/
- CommonAPI::ClientIdList subscribersForTestSelectiveBroadcastSelective_;
- CommonAPI::ClientIdList subscribersForTestBroadcastWithOutArgsSelective_;
+ std::shared_ptr<CommonAPI::ClientIdList> subscribersForTestSelectiveBroadcastSelective_;
+ std::shared_ptr<CommonAPI::ClientIdList> subscribersForTestBroadcastWithOutArgsSelective_;
};
@@ -97,19 +103,19 @@ class TestInterfaceStubRemoteEvent {
virtual ~TestInterfaceStubRemoteEvent() { }
/// Verification callback for remote set requests on the attribute TestPredefinedTypeAttribute
- virtual bool onRemoteSetTestPredefinedTypeAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t TestPredefinedTypeAttribute) = 0;
- /// Action callback for remote set requests on the attribute TestPredefinedTypeAttribute
- virtual void onRemoteTestPredefinedTypeAttributeAttributeChanged() = 0;
+ virtual bool onRemoteSetTestPredefinedTypeAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t TestPredefinedTypeAttribute) = 0;
+ /// Action callback for remote set requests on the attribute TestPredefinedTypeAttribute
+ virtual void onRemoteTestPredefinedTypeAttributeAttributeChanged() = 0;
/// Verification callback for remote set requests on the attribute TestDerivedStructAttribute
- virtual bool onRemoteSetTestDerivedStructAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestStructExtended TestDerivedStructAttribute) = 0;
- /// Action callback for remote set requests on the attribute TestDerivedStructAttribute
- virtual void onRemoteTestDerivedStructAttributeAttributeChanged() = 0;
+ virtual bool onRemoteSetTestDerivedStructAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestStructExtended TestDerivedStructAttribute) = 0;
+ /// Action callback for remote set requests on the attribute TestDerivedStructAttribute
+ virtual void onRemoteTestDerivedStructAttributeAttributeChanged() = 0;
/// Verification callback for remote set requests on the attribute TestDerivedArrayAttribute
- virtual bool onRemoteSetTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 TestDerivedArrayAttribute) = 0;
- /// Action callback for remote set requests on the attribute TestDerivedArrayAttribute
- virtual void onRemoteTestDerivedArrayAttributeAttributeChanged() = 0;
+ virtual bool onRemoteSetTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 TestDerivedArrayAttribute) = 0;
+ /// Action callback for remote set requests on the attribute TestDerivedArrayAttribute
+ virtual void onRemoteTestDerivedArrayAttributeAttributeChanged() = 0;
};
@@ -148,9 +154,9 @@ class TestInterfaceStub : public CommonAPI::Stub<TestInterfaceStubAdapter , Test
* The ClientIds must all be out of the set of subscribed clients.
* If no ClientIds are given, the selective broadcast is sent to all subscribed clients.
*/
- virtual void fireTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers = NULL) = 0;
+ virtual void fireTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL) = 0;
/// retreives the list of all subscribed clients for TestSelectiveBroadcast
- virtual CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective() = 0;
+ virtual std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestSelectiveBroadcastSelective() = 0;
/// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts.
virtual void onTestSelectiveBroadcastSelectiveSubscriptionChanged(const std::shared_ptr<CommonAPI::ClientId> clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event) = 0;
/// Hook method for reacting accepting or denying new subscriptions
@@ -160,13 +166,14 @@ class TestInterfaceStub : public CommonAPI::Stub<TestInterfaceStubAdapter , Test
* The ClientIds must all be out of the set of subscribed clients.
* If no ClientIds are given, the selective broadcast is sent to all subscribed clients.
*/
- virtual void fireTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const CommonAPI::ClientIdList* receivers = NULL) = 0;
+ virtual void fireTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL) = 0;
/// retreives the list of all subscribed clients for TestBroadcastWithOutArgs
- virtual CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective() = 0;
+ virtual std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestBroadcastWithOutArgsSelective() = 0;
/// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts.
virtual void onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(const std::shared_ptr<CommonAPI::ClientId> clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event) = 0;
/// Hook method for reacting accepting or denying new subscriptions
virtual bool onTestBroadcastWithOutArgsSelectiveSubscriptionRequested(const std::shared_ptr<CommonAPI::ClientId> clientId) = 0;
+
};
} // namespace tests
diff --git a/src/test/commonapi/tests/TestInterfaceStubDefault.cpp b/src/test/commonapi/tests/TestInterfaceStubDefault.cpp
index e68bf40..e53fe6a 100644
--- a/src/test/commonapi/tests/TestInterfaceStubDefault.cpp
+++ b/src/test/commonapi/tests/TestInterfaceStubDefault.cpp
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#include <commonapi/tests/TestInterfaceStubDefault.h>
@@ -29,16 +31,9 @@ const uint32_t& TestInterfaceStubDefault::getTestPredefinedTypeAttributeAttribut
void TestInterfaceStubDefault::setTestPredefinedTypeAttributeAttribute(uint32_t value) {
const bool valueChanged = trySetTestPredefinedTypeAttributeAttribute(std::move(value));
- if (valueChanged)
+ if (valueChanged) {
stubAdapter_->fireTestPredefinedTypeAttributeAttributeChanged(testPredefinedTypeAttributeAttributeValue_);
-}
-
-void TestInterfaceStubDefault::setTestPredefinedTypeAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t value) {
- setTestPredefinedTypeAttributeAttribute(value);
-}
-
-void TestInterfaceStubDefault::onRemoteTestPredefinedTypeAttributeAttributeChanged() {
- // No operation in default
+ }
}
bool TestInterfaceStubDefault::trySetTestPredefinedTypeAttributeAttribute(uint32_t value) {
@@ -54,18 +49,26 @@ bool TestInterfaceStubDefault::validateTestPredefinedTypeAttributeAttributeReque
return true;
}
-bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestPredefinedTypeAttributeAttribute(uint32_t value) {
- return defaultStub_->trySetTestPredefinedTypeAttributeAttribute(std::move(value));
+void TestInterfaceStubDefault::setTestPredefinedTypeAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t value) {
+ setTestPredefinedTypeAttributeAttribute(value);
}
-bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestPredefinedTypeAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t value) {
- return onRemoteSetTestPredefinedTypeAttributeAttribute(value);
+void TestInterfaceStubDefault::onRemoteTestPredefinedTypeAttributeAttributeChanged() {
+ // No operation in default
}
void TestInterfaceStubDefault::RemoteEventHandler::onRemoteTestPredefinedTypeAttributeAttributeChanged() {
defaultStub_->onRemoteTestPredefinedTypeAttributeAttributeChanged();
}
+bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestPredefinedTypeAttributeAttribute(uint32_t value) {
+ return defaultStub_->trySetTestPredefinedTypeAttributeAttribute(std::move(value));
+}
+
+bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestPredefinedTypeAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t value) {
+ return onRemoteSetTestPredefinedTypeAttributeAttribute(value);
+}
+
const DerivedTypeCollection::TestStructExtended& TestInterfaceStubDefault::getTestDerivedStructAttributeAttribute() {
return testDerivedStructAttributeAttributeValue_;
}
@@ -76,16 +79,9 @@ const DerivedTypeCollection::TestStructExtended& TestInterfaceStubDefault::getTe
void TestInterfaceStubDefault::setTestDerivedStructAttributeAttribute(DerivedTypeCollection::TestStructExtended value) {
const bool valueChanged = trySetTestDerivedStructAttributeAttribute(std::move(value));
- if (valueChanged)
+ if (valueChanged) {
stubAdapter_->fireTestDerivedStructAttributeAttributeChanged(testDerivedStructAttributeAttributeValue_);
-}
-
-void TestInterfaceStubDefault::setTestDerivedStructAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestStructExtended value) {
- setTestDerivedStructAttributeAttribute(value);
-}
-
-void TestInterfaceStubDefault::onRemoteTestDerivedStructAttributeAttributeChanged() {
- // No operation in default
+ }
}
bool TestInterfaceStubDefault::trySetTestDerivedStructAttributeAttribute(DerivedTypeCollection::TestStructExtended value) {
@@ -101,18 +97,26 @@ bool TestInterfaceStubDefault::validateTestDerivedStructAttributeAttributeReques
return true;
}
-bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedStructAttributeAttribute(DerivedTypeCollection::TestStructExtended value) {
- return defaultStub_->trySetTestDerivedStructAttributeAttribute(std::move(value));
+void TestInterfaceStubDefault::setTestDerivedStructAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestStructExtended value) {
+ setTestDerivedStructAttributeAttribute(value);
}
-bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedStructAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestStructExtended value) {
- return onRemoteSetTestDerivedStructAttributeAttribute(value);
+void TestInterfaceStubDefault::onRemoteTestDerivedStructAttributeAttributeChanged() {
+ // No operation in default
}
void TestInterfaceStubDefault::RemoteEventHandler::onRemoteTestDerivedStructAttributeAttributeChanged() {
defaultStub_->onRemoteTestDerivedStructAttributeAttributeChanged();
}
+bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedStructAttributeAttribute(DerivedTypeCollection::TestStructExtended value) {
+ return defaultStub_->trySetTestDerivedStructAttributeAttribute(std::move(value));
+}
+
+bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedStructAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestStructExtended value) {
+ return onRemoteSetTestDerivedStructAttributeAttribute(value);
+}
+
const DerivedTypeCollection::TestArrayUInt64& TestInterfaceStubDefault::getTestDerivedArrayAttributeAttribute() {
return testDerivedArrayAttributeAttributeValue_;
}
@@ -123,16 +127,9 @@ const DerivedTypeCollection::TestArrayUInt64& TestInterfaceStubDefault::getTestD
void TestInterfaceStubDefault::setTestDerivedArrayAttributeAttribute(DerivedTypeCollection::TestArrayUInt64 value) {
const bool valueChanged = trySetTestDerivedArrayAttributeAttribute(std::move(value));
- if (valueChanged)
+ if (valueChanged) {
stubAdapter_->fireTestDerivedArrayAttributeAttributeChanged(testDerivedArrayAttributeAttributeValue_);
-}
-
-void TestInterfaceStubDefault::setTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 value) {
- setTestDerivedArrayAttributeAttribute(value);
-}
-
-void TestInterfaceStubDefault::onRemoteTestDerivedArrayAttributeAttributeChanged() {
- // No operation in default
+ }
}
bool TestInterfaceStubDefault::trySetTestDerivedArrayAttributeAttribute(DerivedTypeCollection::TestArrayUInt64 value) {
@@ -148,36 +145,64 @@ bool TestInterfaceStubDefault::validateTestDerivedArrayAttributeAttributeRequest
return true;
}
-bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedArrayAttributeAttribute(DerivedTypeCollection::TestArrayUInt64 value) {
- return defaultStub_->trySetTestDerivedArrayAttributeAttribute(std::move(value));
+void TestInterfaceStubDefault::setTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 value) {
+ setTestDerivedArrayAttributeAttribute(value);
}
-bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 value) {
- return onRemoteSetTestDerivedArrayAttributeAttribute(value);
+void TestInterfaceStubDefault::onRemoteTestDerivedArrayAttributeAttributeChanged() {
+ // No operation in default
}
void TestInterfaceStubDefault::RemoteEventHandler::onRemoteTestDerivedArrayAttributeAttributeChanged() {
defaultStub_->onRemoteTestDerivedArrayAttributeAttributeChanged();
}
+bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedArrayAttributeAttribute(DerivedTypeCollection::TestArrayUInt64 value) {
+ return defaultStub_->trySetTestDerivedArrayAttributeAttribute(std::move(value));
+}
+
+bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 value) {
+ return onRemoteSetTestDerivedArrayAttributeAttribute(value);
+}
+
void TestInterfaceStubDefault::testEmptyMethod(const std::shared_ptr<CommonAPI::ClientId> clientId) {
+ // Call old style methods in default
+ testEmptyMethod();
+}
+void TestInterfaceStubDefault::testEmptyMethod() {
// No operation in default
}
void TestInterfaceStubDefault::testVoidPredefinedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t uint32Value, std::string stringValue) {
+ // Call old style methods in default
+ testVoidPredefinedTypeMethod(uint32Value, stringValue);
+}
+void TestInterfaceStubDefault::testVoidPredefinedTypeMethod(uint32_t uint32Value, std::string stringValue) {
// No operation in default
}
void TestInterfaceStubDefault::testPredefinedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t uint32InValue, std::string stringInValue, uint32_t& uint32OutValue, std::string& stringOutValue) {
+ // Call old style methods in default
+ testPredefinedTypeMethod(uint32InValue, stringInValue, uint32OutValue, stringOutValue);
+}
+void TestInterfaceStubDefault::testPredefinedTypeMethod(uint32_t uint32InValue, std::string stringInValue, uint32_t& uint32OutValue, std::string& stringOutValue) {
// No operation in default
}
void TestInterfaceStubDefault::testVoidDerivedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestEnumExtended2 testEnumExtended2Value, DerivedTypeCollection::TestMap testMapValue) {
+ // Call old style methods in default
+ testVoidDerivedTypeMethod(testEnumExtended2Value, testMapValue);
+}
+void TestInterfaceStubDefault::testVoidDerivedTypeMethod(DerivedTypeCollection::TestEnumExtended2 testEnumExtended2Value, DerivedTypeCollection::TestMap testMapValue) {
// No operation in default
}
void TestInterfaceStubDefault::testDerivedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestEnumExtended2 testEnumExtended2InValue, DerivedTypeCollection::TestMap testMapInValue, DerivedTypeCollection::TestEnumExtended2& testEnumExtended2OutValue, DerivedTypeCollection::TestMap& testMapOutValue) {
+ // Call old style methods in default
+ testDerivedTypeMethod(testEnumExtended2InValue, testMapInValue, testEnumExtended2OutValue, testMapOutValue);
+}
+void TestInterfaceStubDefault::testDerivedTypeMethod(DerivedTypeCollection::TestEnumExtended2 testEnumExtended2InValue, DerivedTypeCollection::TestMap testMapInValue, DerivedTypeCollection::TestEnumExtended2& testEnumExtended2OutValue, DerivedTypeCollection::TestMap& testMapOutValue) {
// No operation in default
}
@@ -185,7 +210,7 @@ void TestInterfaceStubDefault::testDerivedTypeMethod(const std::shared_ptr<Commo
void TestInterfaceStubDefault::fireTestPredefinedTypeBroadcastEvent(const uint32_t& uint32Value, const std::string& stringValue) {
stubAdapter_->fireTestPredefinedTypeBroadcastEvent(uint32Value, stringValue);
}
-void TestInterfaceStubDefault::fireTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers) {
+void TestInterfaceStubDefault::fireTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientIdList> receivers) {
stubAdapter_->sendTestSelectiveBroadcastSelective(receivers);
}
void TestInterfaceStubDefault::onTestSelectiveBroadcastSelectiveSubscriptionChanged(const std::shared_ptr<CommonAPI::ClientId> clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event) {
@@ -195,11 +220,11 @@ bool TestInterfaceStubDefault::onTestSelectiveBroadcastSelectiveSubscriptionRequ
// Accept in default
return true;
}
-CommonAPI::ClientIdList* const TestInterfaceStubDefault::getSubscribersForTestSelectiveBroadcastSelective() {
+std::shared_ptr<CommonAPI::ClientIdList> const TestInterfaceStubDefault::getSubscribersForTestSelectiveBroadcastSelective() {
return(stubAdapter_->getSubscribersForTestSelectiveBroadcastSelective());
}
-void TestInterfaceStubDefault::fireTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const CommonAPI::ClientIdList* receivers) {
+void TestInterfaceStubDefault::fireTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const std::shared_ptr<CommonAPI::ClientIdList> receivers) {
stubAdapter_->sendTestBroadcastWithOutArgsSelective(uint32Value, stringValue, receivers);
}
void TestInterfaceStubDefault::onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(const std::shared_ptr<CommonAPI::ClientId> clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event) {
@@ -209,11 +234,12 @@ bool TestInterfaceStubDefault::onTestBroadcastWithOutArgsSelectiveSubscriptionRe
// Accept in default
return true;
}
-CommonAPI::ClientIdList* const TestInterfaceStubDefault::getSubscribersForTestBroadcastWithOutArgsSelective() {
+std::shared_ptr<CommonAPI::ClientIdList> const TestInterfaceStubDefault::getSubscribersForTestBroadcastWithOutArgsSelective() {
return(stubAdapter_->getSubscribersForTestBroadcastWithOutArgsSelective());
}
+
TestInterfaceStubDefault::RemoteEventHandler::RemoteEventHandler(TestInterfaceStubDefault* defaultStub):
defaultStub_(defaultStub) {
}
diff --git a/src/test/commonapi/tests/TestInterfaceStubDefault.h b/src/test/commonapi/tests/TestInterfaceStubDefault.h
index 0128b8e..33853f7 100644
--- a/src/test/commonapi/tests/TestInterfaceStubDefault.h
+++ b/src/test/commonapi/tests/TestInterfaceStubDefault.h
@@ -1,14 +1,17 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef COMMONAPI_TESTS_Test_Interface_STUB_DEFAULT_H_
#define COMMONAPI_TESTS_Test_Interface_STUB_DEFAULT_H_
#include <commonapi/tests/TestInterfaceStub.h>
+#include <sstream>
namespace commonapi {
namespace tests {
@@ -43,40 +46,46 @@ class TestInterfaceStubDefault : public TestInterfaceStub {
virtual void setTestDerivedArrayAttributeAttribute(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestArrayUInt64 value);
virtual void testEmptyMethod(const std::shared_ptr<CommonAPI::ClientId> clientId);
+ virtual void testEmptyMethod();
virtual void testVoidPredefinedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t uint32Value, std::string stringValue);
+ virtual void testVoidPredefinedTypeMethod(uint32_t uint32Value, std::string stringValue);
virtual void testPredefinedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, uint32_t uint32InValue, std::string stringInValue, uint32_t& uint32OutValue, std::string& stringOutValue);
+ virtual void testPredefinedTypeMethod(uint32_t uint32InValue, std::string stringInValue, uint32_t& uint32OutValue, std::string& stringOutValue);
virtual void testVoidDerivedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestEnumExtended2 testEnumExtended2Value, DerivedTypeCollection::TestMap testMapValue);
+ virtual void testVoidDerivedTypeMethod(DerivedTypeCollection::TestEnumExtended2 testEnumExtended2Value, DerivedTypeCollection::TestMap testMapValue);
virtual void testDerivedTypeMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, DerivedTypeCollection::TestEnumExtended2 testEnumExtended2InValue, DerivedTypeCollection::TestMap testMapInValue, DerivedTypeCollection::TestEnumExtended2& testEnumExtended2OutValue, DerivedTypeCollection::TestMap& testMapOutValue);
+ virtual void testDerivedTypeMethod(DerivedTypeCollection::TestEnumExtended2 testEnumExtended2InValue, DerivedTypeCollection::TestMap testMapInValue, DerivedTypeCollection::TestEnumExtended2& testEnumExtended2OutValue, DerivedTypeCollection::TestMap& testMapOutValue);
virtual void fireTestPredefinedTypeBroadcastEvent(const uint32_t& uint32Value, const std::string& stringValue);
- virtual void fireTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers = NULL);
- virtual CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective();
+ virtual void fireTestSelectiveBroadcastSelective(const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL);
+ virtual std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestSelectiveBroadcastSelective();
/// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts.
virtual void onTestSelectiveBroadcastSelectiveSubscriptionChanged(const std::shared_ptr<CommonAPI::ClientId> clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event);
/// Hook method for reacting accepting or denying new subscriptions
virtual bool onTestSelectiveBroadcastSelectiveSubscriptionRequested(const std::shared_ptr<CommonAPI::ClientId> clientId);
- virtual void fireTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const CommonAPI::ClientIdList* receivers = NULL);
- virtual CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective();
+ virtual void fireTestBroadcastWithOutArgsSelective(const uint32_t& uint32Value, const std::string& stringValue, const std::shared_ptr<CommonAPI::ClientIdList> receivers = NULL);
+ virtual std::shared_ptr<CommonAPI::ClientIdList> const getSubscribersForTestBroadcastWithOutArgsSelective();
/// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts.
virtual void onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(const std::shared_ptr<CommonAPI::ClientId> clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event);
/// Hook method for reacting accepting or denying new subscriptions
virtual bool onTestBroadcastWithOutArgsSelectiveSubscriptionRequested(const std::shared_ptr<CommonAPI::ClientId> clientId);
+
protected:
- virtual void onRemoteTestPredefinedTypeAttributeAttributeChanged();
virtual bool trySetTestPredefinedTypeAttributeAttribute(uint32_t value);
virtual bool validateTestPredefinedTypeAttributeAttributeRequestedValue(const uint32_t& value);
- virtual void onRemoteTestDerivedStructAttributeAttributeChanged();
+ virtual void onRemoteTestPredefinedTypeAttributeAttributeChanged();
virtual bool trySetTestDerivedStructAttributeAttribute(DerivedTypeCollection::TestStructExtended value);
virtual bool validateTestDerivedStructAttributeAttributeRequestedValue(const DerivedTypeCollection::TestStructExtended& value);
- virtual void onRemoteTestDerivedArrayAttributeAttributeChanged();
+ virtual void onRemoteTestDerivedStructAttributeAttributeChanged();
virtual bool trySetTestDerivedArrayAttributeAttribute(DerivedTypeCollection::TestArrayUInt64 value);
virtual bool validateTestDerivedArrayAttributeAttributeRequestedValue(const DerivedTypeCollection::TestArrayUInt64& value);
+ virtual void onRemoteTestDerivedArrayAttributeAttributeChanged();
std::shared_ptr<TestInterfaceStubAdapter> stubAdapter_;
private:
class RemoteEventHandler: public TestInterfaceStubRemoteEvent {
diff --git a/src/test/commonapi/tests/managed/BranchInterface.h b/src/test/commonapi/tests/managed/BranchInterface.h
new file mode 100644
index 0000000..80b5138
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterface.h
@@ -0,0 +1,90 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_H_
+
+
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/InputStream.h>
+#include <CommonAPI/OutputStream.h>
+#include <CommonAPI/types.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class BranchInterface {
+ public:
+ virtual ~BranchInterface() { }
+
+ static inline const char* getInterfaceId();
+ static inline CommonAPI::Version getInterfaceVersion();
+ enum class testBranchMethodError: int32_t {
+ OK,
+ NOTOK
+ };
+
+ // Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
+ struct testBranchMethodErrorComparator;
+};
+
+const char* BranchInterface::getInterfaceId() {
+ static const char* interfaceId = "commonapi.tests.managed.BranchInterface";
+ return interfaceId;
+}
+
+CommonAPI::Version BranchInterface::getInterfaceVersion() {
+ return CommonAPI::Version(1, 0);
+}
+
+inline CommonAPI::InputStream& operator>>(CommonAPI::InputStream& inputStream, BranchInterface::testBranchMethodError& enumValue) {
+ return inputStream.readEnumValue<int32_t>(enumValue);
+}
+
+inline CommonAPI::OutputStream& operator<<(CommonAPI::OutputStream& outputStream, const BranchInterface::testBranchMethodError& enumValue) {
+ return outputStream.writeEnumValue(static_cast<int32_t>(enumValue));
+}
+
+struct BranchInterface::testBranchMethodErrorComparator {
+ inline bool operator()(const testBranchMethodError& lhs, const testBranchMethodError& rhs) const {
+ return static_cast<int32_t>(lhs) < static_cast<int32_t>(rhs);
+ }
+};
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+namespace CommonAPI {
+
+}
+
+
+namespace std {
+ //hashes for types
+
+ //hashes for error types
+ //Hash for testBranchMethodError
+ template<>
+ struct hash<commonapi::tests::managed::BranchInterface::testBranchMethodError> {
+ inline size_t operator()(const commonapi::tests::managed::BranchInterface::testBranchMethodError& testBranchMethodError) const {
+ return static_cast<int32_t>(testBranchMethodError);
+ }
+ };
+}
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_H_
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.cpp b/src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.cpp
new file mode 100644
index 0000000..f056c50
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.cpp
@@ -0,0 +1,74 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "BranchInterfaceDBusProxy.h"
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+std::shared_ptr<CommonAPI::DBus::DBusProxy> createBranchInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection) {
+ return std::make_shared<BranchInterfaceDBusProxy>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
+}
+
+__attribute__((constructor)) void registerBranchInterfaceDBusProxy(void) {
+ CommonAPI::DBus::DBusFactory::registerProxyFactoryMethod(BranchInterface::getInterfaceId(),
+ &createBranchInterfaceDBusProxy);
+}
+
+BranchInterfaceDBusProxy::BranchInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection):
+ CommonAPI::DBus::DBusProxy(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
+ {
+ }
+
+
+
+void BranchInterfaceDBusProxy::testBranchMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString) {
+ CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<int32_t, std::string>,
+ CommonAPI::DBus::DBusSerializableArguments<BranchInterface::testBranchMethodError, int32_t, std::string> >::callMethodWithReply(
+ *this,
+ "testBranchMethod",
+ "is",
+ inInt, inString,
+ callStatus,
+ methodError
+ , outInt, outString);
+}
+std::future<CommonAPI::CallStatus> BranchInterfaceDBusProxy::testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback) {
+ return CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<int32_t, std::string>,
+ CommonAPI::DBus::DBusSerializableArguments<BranchInterface::testBranchMethodError, int32_t, std::string> >::callMethodAsync(
+ *this,
+ "testBranchMethod",
+ "is",
+ inInt, inString,
+ std::move(callback));
+}
+
+
+
+void BranchInterfaceDBusProxy::getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const {
+ ownVersionMajor = 1;
+ ownVersionMinor = 0;
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.h b/src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.h
new file mode 100644
index 0000000..a5eac7b
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.h
@@ -0,0 +1,61 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_DBUS_PROXY_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_DBUS_PROXY_H_
+
+#include <commonapi/tests/managed/BranchInterfaceProxyBase.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusProxy.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+#include <string>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class BranchInterfaceDBusProxy: virtual public BranchInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy {
+ public:
+ BranchInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection);
+
+ virtual ~BranchInterfaceDBusProxy() { }
+
+
+
+ virtual void testBranchMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString);
+ virtual std::future<CommonAPI::CallStatus> testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback);
+
+
+ virtual void getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const;
+
+ private:
+
+
+};
+
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_DBUS_PROXY_H_
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.cpp b/src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.cpp
new file mode 100644
index 0000000..b9c846b
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.cpp
@@ -0,0 +1,89 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "BranchInterfaceDBusStubAdapter.h"
+#include <commonapi/tests/managed/BranchInterface.h>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> createBranchInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stubBase) {
+ return std::make_shared<BranchInterfaceDBusStubAdapter>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase);
+}
+
+__attribute__((constructor)) void registerBranchInterfaceDBusStubAdapter(void) {
+ CommonAPI::DBus::DBusFactory::registerAdapterFactoryMethod(BranchInterface::getInterfaceId(),
+ &createBranchInterfaceDBusStubAdapter);
+}
+
+BranchInterfaceDBusStubAdapter::BranchInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub):
+ BranchInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath,
+ dbusConnection, std::dynamic_pointer_cast<BranchInterfaceStub>(stub),
+ NULL)
+ {
+}
+
+BranchInterfaceDBusStubAdapter::~BranchInterfaceDBusStubAdapter() {
+ deactivateManagedInstances();
+ deinit();
+ stub_.reset();
+}
+
+void BranchInterfaceDBusStubAdapter::deactivateManagedInstances() {
+}
+
+const char* BranchInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const {
+ static const char* introspectionData =
+ "<method name=\"testBranchMethod\">\n"
+ "<arg name=\"inInt\" type=\"i\" direction=\"in\" />\n"
+ "<arg name=\"inString\" type=\"s\" direction=\"in\" />\n"
+ "<arg name=\"methodError\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"outInt\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"outString\" type=\"s\" direction=\"out\" />\n"
+ "</method>\n"
+ ;
+ return introspectionData;
+}
+
+
+
+static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher<
+ BranchInterfaceStub,
+ std::tuple<int32_t, std::string>,
+ std::tuple<BranchInterface::testBranchMethodError, int32_t, std::string>
+ > testBranchMethodStubDispatcher(&BranchInterfaceStub::testBranchMethod, "iis");
+
+
+
+const BranchInterfaceDBusStubAdapter::StubDispatcherTable& BranchInterfaceDBusStubAdapter::getStubDispatcherTable() {
+ static const BranchInterfaceDBusStubAdapter::StubDispatcherTable stubDispatcherTable = {
+ { { "testBranchMethod", "is" }, &commonapi::tests::managed::testBranchMethodStubDispatcher }
+ };
+ return stubDispatcherTable;
+}
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.h b/src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.h
new file mode 100644
index 0000000..df42f7b
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.h
@@ -0,0 +1,62 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_DBUS_STUB_ADAPTER_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_DBUS_STUB_ADAPTER_H_
+
+#include <commonapi/tests/managed/BranchInterfaceStub.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusStubAdapterHelper.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+typedef CommonAPI::DBus::DBusStubAdapterHelper<BranchInterfaceStub> BranchInterfaceDBusStubAdapterHelper;
+
+class BranchInterfaceDBusStubAdapter: public BranchInterfaceStubAdapter, public BranchInterfaceDBusStubAdapterHelper {
+ public:
+ BranchInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub);
+
+ ~BranchInterfaceDBusStubAdapter();
+
+
+
+
+ const StubDispatcherTable& getStubDispatcherTable();
+
+ void deactivateManagedInstances();
+
+ protected:
+ virtual const char* getMethodsDBusIntrospectionXmlData() const;
+
+ private:
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_DBUS_STUB_ADAPTER_H_
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceProxy.h b/src/test/commonapi/tests/managed/BranchInterfaceProxy.h
new file mode 100644
index 0000000..9bbbc77
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceProxy.h
@@ -0,0 +1,174 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_PROXY_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_PROXY_H_
+
+#include "BranchInterfaceProxyBase.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+template <typename ... _AttributeExtensions>
+class BranchInterfaceProxy: virtual public BranchInterface, virtual public BranchInterfaceProxyBase, public _AttributeExtensions... {
+ public:
+ BranchInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate);
+ ~BranchInterfaceProxy();
+
+
+
+ /**
+ * Calls testBranchMethod with synchronous semantics.
+ *
+ * All const parameters are input parameters to this method.
+ * All non-const parameters will be filled with the returned values.
+ * The CallStatus will be filled when the method returns and indicate either
+ * "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
+ * will be set.
+ */
+ virtual void testBranchMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString);
+ /**
+ * Calls testBranchMethod with asynchronous semantics.
+ *
+ * The provided callback will be called when the reply to this call arrives or
+ * an error occurs during the call. The CallStatus will indicate either "SUCCESS"
+ * or which type of error has occurred. In case of any error, ONLY the CallStatus
+ * will have a defined value.
+ * The std::future returned by this method will be fulfilled at arrival of the reply.
+ * It will provide the same value for CallStatus as will be handed to the callback.
+ */
+ virtual std::future<CommonAPI::CallStatus> testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback);
+
+
+ /**
+ * Returns the CommonAPI address of the remote partner this proxy communicates with.
+ */
+ virtual std::string getAddress() const;
+
+ /**
+ * Returns the domain of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getDomain() const;
+
+ /**
+ * Returns the service ID of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getServiceId() const;
+
+ /**
+ * Returns the instance ID of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getInstanceId() const;
+
+ /**
+ * Returns true if the remote partner for this proxy is currently known to be available.
+ */
+ virtual bool isAvailable() const;
+
+ /**
+ * Returns true if the remote partner for this proxy is available.
+ */
+ virtual bool isAvailableBlocking() const;
+
+ /**
+ * Returns the wrapper class that is used to (de-)register for notifications about
+ * the availability of the remote partner of this proxy.
+ */
+ virtual CommonAPI::ProxyStatusEvent& getProxyStatusEvent();
+
+ /**
+ * Returns the wrapper class that is used to access version information of the remote
+ * partner of this proxy.
+ */
+ virtual CommonAPI::InterfaceVersionAttribute& getInterfaceVersionAttribute();
+
+ private:
+ std::shared_ptr<BranchInterfaceProxyBase> delegate_;
+};
+
+
+//
+// BranchInterfaceProxy Implementation
+//
+template <typename ... _AttributeExtensions>
+BranchInterfaceProxy<_AttributeExtensions...>::BranchInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate):
+ delegate_(std::dynamic_pointer_cast<BranchInterfaceProxyBase>(delegate)),
+ _AttributeExtensions(*(std::dynamic_pointer_cast<BranchInterfaceProxyBase>(delegate)))... {
+}
+
+template <typename ... _AttributeExtensions>
+BranchInterfaceProxy<_AttributeExtensions...>::~BranchInterfaceProxy() {
+}
+
+template <typename ... _AttributeExtensions>
+void BranchInterfaceProxy<_AttributeExtensions...>::testBranchMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString) {
+ delegate_->testBranchMethod(inInt, inString, callStatus, methodError, outInt, outString);
+}
+
+template <typename ... _AttributeExtensions>
+std::future<CommonAPI::CallStatus> BranchInterfaceProxy<_AttributeExtensions...>::testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback) {
+ return delegate_->testBranchMethodAsync(inInt, inString, callback);
+}
+
+template <typename ... _AttributeExtensions>
+std::string BranchInterfaceProxy<_AttributeExtensions...>::getAddress() const {
+ return delegate_->getAddress();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& BranchInterfaceProxy<_AttributeExtensions...>::getDomain() const {
+ return delegate_->getDomain();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& BranchInterfaceProxy<_AttributeExtensions...>::getServiceId() const {
+ return delegate_->getServiceId();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& BranchInterfaceProxy<_AttributeExtensions...>::getInstanceId() const {
+ return delegate_->getInstanceId();
+}
+
+template <typename ... _AttributeExtensions>
+bool BranchInterfaceProxy<_AttributeExtensions...>::isAvailable() const {
+ return delegate_->isAvailable();
+}
+
+template <typename ... _AttributeExtensions>
+bool BranchInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const {
+ return delegate_->isAvailableBlocking();
+}
+
+template <typename ... _AttributeExtensions>
+CommonAPI::ProxyStatusEvent& BranchInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() {
+ return delegate_->getProxyStatusEvent();
+}
+
+template <typename ... _AttributeExtensions>
+CommonAPI::InterfaceVersionAttribute& BranchInterfaceProxy<_AttributeExtensions...>::getInterfaceVersionAttribute() {
+ return delegate_->getInterfaceVersionAttribute();
+}
+
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_PROXY_H_
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceProxyBase.h b/src/test/commonapi/tests/managed/BranchInterfaceProxyBase.h
new file mode 100644
index 0000000..5a6a613
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceProxyBase.h
@@ -0,0 +1,47 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_PROXY_BASE_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_PROXY_BASE_H_
+
+#include "BranchInterface.h"
+
+
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Proxy.h>
+#include <functional>
+#include <future>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class BranchInterfaceProxyBase: virtual public CommonAPI::Proxy {
+ public:
+
+ typedef std::function<void(const CommonAPI::CallStatus&, const BranchInterface::testBranchMethodError&, const int32_t&, const std::string&)> TestBranchMethodAsyncCallback;
+
+
+
+ virtual void testBranchMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString) = 0;
+ virtual std::future<CommonAPI::CallStatus> testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback) = 0;
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_PROXY_BASE_H_
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceStub.h b/src/test/commonapi/tests/managed/BranchInterfaceStub.h
new file mode 100644
index 0000000..8f90cfa
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceStub.h
@@ -0,0 +1,91 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_STUB_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_STUB_H_
+
+
+
+
+#include "BranchInterface.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Stub.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+/**
+ * Receives messages from remote and handles all dispatching of deserialized calls
+ * to a stub for the service BranchInterface. Also provides means to send broadcasts
+ * and attribute-changed-notifications of observable attributes as defined by this service.
+ * An application developer should not need to bother with this class.
+ */
+class BranchInterfaceStubAdapter: virtual public CommonAPI::StubAdapter, public BranchInterface {
+ public:
+
+
+
+ virtual void deactivateManagedInstances() = 0;
+
+protected:
+ /**
+ * Defines properties for storing the ClientIds of clients / proxies that have
+ * subscribed to the selective broadcasts
+ */
+};
+
+
+/**
+ * Defines the necessary callbacks to handle remote set events related to the attributes
+ * defined in the IDL description for BranchInterface.
+ * For each attribute two callbacks are defined:
+ * - a verification callback that allows to verify the requested value and to prevent setting
+ * e.g. an invalid value ("onRemoteSet<AttributeName>").
+ * - an action callback to do local work after the attribute value has been changed
+ * ("onRemote<AttributeName>Changed").
+ *
+ * This class and the one below are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class BranchInterfaceStubRemoteEvent {
+ public:
+ virtual ~BranchInterfaceStubRemoteEvent() { }
+
+};
+
+
+/**
+ * Defines the interface that must be implemented by any class that should provide
+ * the service BranchInterface to remote clients.
+ * This class and the one above are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class BranchInterfaceStub : public CommonAPI::Stub<BranchInterfaceStubAdapter , BranchInterfaceStubRemoteEvent> {
+ public:
+ virtual ~BranchInterfaceStub() { }
+
+
+ /// This is the method that will be called on remote calls on the method testBranchMethod.
+ virtual void testBranchMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString) = 0;
+
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_STUB_H_
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceStubDefault.cpp b/src/test/commonapi/tests/managed/BranchInterfaceStubDefault.cpp
new file mode 100644
index 0000000..1fa2a8f
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceStubDefault.cpp
@@ -0,0 +1,43 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include <commonapi/tests/managed/BranchInterfaceStubDefault.h>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+BranchInterfaceStubDefault::BranchInterfaceStubDefault():
+ remoteEventHandler_(this) {
+}
+
+BranchInterfaceStubRemoteEvent* BranchInterfaceStubDefault::initStubAdapter(const std::shared_ptr<BranchInterfaceStubAdapter>& stubAdapter) {
+ stubAdapter_ = stubAdapter;
+ return &remoteEventHandler_;
+}
+
+
+void BranchInterfaceStubDefault::testBranchMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString) {
+ // Call old style methods in default
+ testBranchMethod(inInt, inString, methodError, outInt, outString);
+}
+void BranchInterfaceStubDefault::testBranchMethod(int32_t inInt, std::string inString, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString) {
+ // No operation in default
+}
+
+
+
+
+BranchInterfaceStubDefault::RemoteEventHandler::RemoteEventHandler(BranchInterfaceStubDefault* defaultStub):
+ defaultStub_(defaultStub) {
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/BranchInterfaceStubDefault.h b/src/test/commonapi/tests/managed/BranchInterfaceStubDefault.h
new file mode 100644
index 0000000..b2dab45
--- /dev/null
+++ b/src/test/commonapi/tests/managed/BranchInterfaceStubDefault.h
@@ -0,0 +1,63 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Branch_Interface_STUB_DEFAULT_H_
+#define COMMONAPI_TESTS_MANAGED_Branch_Interface_STUB_DEFAULT_H_
+
+#include <commonapi/tests/managed/BranchInterfaceStub.h>
+#include <sstream>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+/**
+ * Provides a default implementation for BranchInterfaceStubRemoteEvent and
+ * BranchInterfaceStub. Method callbacks have an empty implementation,
+ * remote set calls on attributes will always change the value of the attribute
+ * to the one received.
+ *
+ * Override this stub if you only want to provide a subset of the functionality
+ * that would be defined for this service, and/or if you do not need any non-default
+ * behaviour.
+ */
+class BranchInterfaceStubDefault : public BranchInterfaceStub {
+ public:
+ BranchInterfaceStubDefault();
+
+ BranchInterfaceStubRemoteEvent* initStubAdapter(const std::shared_ptr<BranchInterfaceStubAdapter>& stubAdapter);
+
+
+ virtual void testBranchMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString);
+ virtual void testBranchMethod(int32_t inInt, std::string inString, BranchInterface::testBranchMethodError& methodError, int32_t& outInt, std::string& outString);
+
+
+
+
+ protected:
+ std::shared_ptr<BranchInterfaceStubAdapter> stubAdapter_;
+ private:
+ class RemoteEventHandler: public BranchInterfaceStubRemoteEvent {
+ public:
+ RemoteEventHandler(BranchInterfaceStubDefault* defaultStub);
+
+
+ private:
+ BranchInterfaceStubDefault* defaultStub_;
+ };
+
+ RemoteEventHandler remoteEventHandler_;
+
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Branch_Interface_STUB_DEFAULT_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterface.h b/src/test/commonapi/tests/managed/LeafInterface.h
new file mode 100644
index 0000000..1aaad3d
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterface.h
@@ -0,0 +1,90 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_H_
+
+
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/InputStream.h>
+#include <CommonAPI/OutputStream.h>
+#include <CommonAPI/types.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class LeafInterface {
+ public:
+ virtual ~LeafInterface() { }
+
+ static inline const char* getInterfaceId();
+ static inline CommonAPI::Version getInterfaceVersion();
+ enum class testLeafMethodError: int32_t {
+ OK,
+ NOTOK
+ };
+
+ // Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
+ struct testLeafMethodErrorComparator;
+};
+
+const char* LeafInterface::getInterfaceId() {
+ static const char* interfaceId = "commonapi.tests.managed.LeafInterface";
+ return interfaceId;
+}
+
+CommonAPI::Version LeafInterface::getInterfaceVersion() {
+ return CommonAPI::Version(1, 0);
+}
+
+inline CommonAPI::InputStream& operator>>(CommonAPI::InputStream& inputStream, LeafInterface::testLeafMethodError& enumValue) {
+ return inputStream.readEnumValue<int32_t>(enumValue);
+}
+
+inline CommonAPI::OutputStream& operator<<(CommonAPI::OutputStream& outputStream, const LeafInterface::testLeafMethodError& enumValue) {
+ return outputStream.writeEnumValue(static_cast<int32_t>(enumValue));
+}
+
+struct LeafInterface::testLeafMethodErrorComparator {
+ inline bool operator()(const testLeafMethodError& lhs, const testLeafMethodError& rhs) const {
+ return static_cast<int32_t>(lhs) < static_cast<int32_t>(rhs);
+ }
+};
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+namespace CommonAPI {
+
+}
+
+
+namespace std {
+ //hashes for types
+
+ //hashes for error types
+ //Hash for testLeafMethodError
+ template<>
+ struct hash<commonapi::tests::managed::LeafInterface::testLeafMethodError> {
+ inline size_t operator()(const commonapi::tests::managed::LeafInterface::testLeafMethodError& testLeafMethodError) const {
+ return static_cast<int32_t>(testLeafMethodError);
+ }
+ };
+}
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.cpp b/src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.cpp
new file mode 100644
index 0000000..a87c019
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.cpp
@@ -0,0 +1,74 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "LeafInterfaceDBusProxy.h"
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+std::shared_ptr<CommonAPI::DBus::DBusProxy> createLeafInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection) {
+ return std::make_shared<LeafInterfaceDBusProxy>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
+}
+
+__attribute__((constructor)) void registerLeafInterfaceDBusProxy(void) {
+ CommonAPI::DBus::DBusFactory::registerProxyFactoryMethod(LeafInterface::getInterfaceId(),
+ &createLeafInterfaceDBusProxy);
+}
+
+LeafInterfaceDBusProxy::LeafInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection):
+ CommonAPI::DBus::DBusProxy(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
+ {
+ }
+
+
+
+void LeafInterfaceDBusProxy::testLeafMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString) {
+ CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<int32_t, std::string>,
+ CommonAPI::DBus::DBusSerializableArguments<LeafInterface::testLeafMethodError, int32_t, std::string> >::callMethodWithReply(
+ *this,
+ "testLeafMethod",
+ "is",
+ inInt, inString,
+ callStatus,
+ methodError
+ , outInt, outString);
+}
+std::future<CommonAPI::CallStatus> LeafInterfaceDBusProxy::testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback) {
+ return CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<int32_t, std::string>,
+ CommonAPI::DBus::DBusSerializableArguments<LeafInterface::testLeafMethodError, int32_t, std::string> >::callMethodAsync(
+ *this,
+ "testLeafMethod",
+ "is",
+ inInt, inString,
+ std::move(callback));
+}
+
+
+
+void LeafInterfaceDBusProxy::getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const {
+ ownVersionMajor = 1;
+ ownVersionMinor = 0;
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.h b/src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.h
new file mode 100644
index 0000000..8653db4
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.h
@@ -0,0 +1,61 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_DBUS_PROXY_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_DBUS_PROXY_H_
+
+#include <commonapi/tests/managed/LeafInterfaceProxyBase.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusProxy.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+#include <string>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class LeafInterfaceDBusProxy: virtual public LeafInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy {
+ public:
+ LeafInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection);
+
+ virtual ~LeafInterfaceDBusProxy() { }
+
+
+
+ virtual void testLeafMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString);
+ virtual std::future<CommonAPI::CallStatus> testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback);
+
+
+ virtual void getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const;
+
+ private:
+
+
+};
+
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_DBUS_PROXY_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.cpp b/src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.cpp
new file mode 100644
index 0000000..2ff0d48
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.cpp
@@ -0,0 +1,89 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "LeafInterfaceDBusStubAdapter.h"
+#include <commonapi/tests/managed/LeafInterface.h>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> createLeafInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stubBase) {
+ return std::make_shared<LeafInterfaceDBusStubAdapter>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase);
+}
+
+__attribute__((constructor)) void registerLeafInterfaceDBusStubAdapter(void) {
+ CommonAPI::DBus::DBusFactory::registerAdapterFactoryMethod(LeafInterface::getInterfaceId(),
+ &createLeafInterfaceDBusStubAdapter);
+}
+
+LeafInterfaceDBusStubAdapter::LeafInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub):
+ LeafInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath,
+ dbusConnection, std::dynamic_pointer_cast<LeafInterfaceStub>(stub),
+ NULL)
+ {
+}
+
+LeafInterfaceDBusStubAdapter::~LeafInterfaceDBusStubAdapter() {
+ deactivateManagedInstances();
+ deinit();
+ stub_.reset();
+}
+
+void LeafInterfaceDBusStubAdapter::deactivateManagedInstances() {
+}
+
+const char* LeafInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const {
+ static const char* introspectionData =
+ "<method name=\"testLeafMethod\">\n"
+ "<arg name=\"inInt\" type=\"i\" direction=\"in\" />\n"
+ "<arg name=\"inString\" type=\"s\" direction=\"in\" />\n"
+ "<arg name=\"methodError\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"outInt\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"outString\" type=\"s\" direction=\"out\" />\n"
+ "</method>\n"
+ ;
+ return introspectionData;
+}
+
+
+
+static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher<
+ LeafInterfaceStub,
+ std::tuple<int32_t, std::string>,
+ std::tuple<LeafInterface::testLeafMethodError, int32_t, std::string>
+ > testLeafMethodStubDispatcher(&LeafInterfaceStub::testLeafMethod, "iis");
+
+
+
+const LeafInterfaceDBusStubAdapter::StubDispatcherTable& LeafInterfaceDBusStubAdapter::getStubDispatcherTable() {
+ static const LeafInterfaceDBusStubAdapter::StubDispatcherTable stubDispatcherTable = {
+ { { "testLeafMethod", "is" }, &commonapi::tests::managed::testLeafMethodStubDispatcher }
+ };
+ return stubDispatcherTable;
+}
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.h b/src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.h
new file mode 100644
index 0000000..f67cc26
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.h
@@ -0,0 +1,62 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_DBUS_STUB_ADAPTER_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_DBUS_STUB_ADAPTER_H_
+
+#include <commonapi/tests/managed/LeafInterfaceStub.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusStubAdapterHelper.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+typedef CommonAPI::DBus::DBusStubAdapterHelper<LeafInterfaceStub> LeafInterfaceDBusStubAdapterHelper;
+
+class LeafInterfaceDBusStubAdapter: public LeafInterfaceStubAdapter, public LeafInterfaceDBusStubAdapterHelper {
+ public:
+ LeafInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub);
+
+ ~LeafInterfaceDBusStubAdapter();
+
+
+
+
+ const StubDispatcherTable& getStubDispatcherTable();
+
+ void deactivateManagedInstances();
+
+ protected:
+ virtual const char* getMethodsDBusIntrospectionXmlData() const;
+
+ private:
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_DBUS_STUB_ADAPTER_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceProxy.h b/src/test/commonapi/tests/managed/LeafInterfaceProxy.h
new file mode 100644
index 0000000..6001346
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceProxy.h
@@ -0,0 +1,174 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+ * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+ * If a copy of the MPL was not distributed with this file, You can obtain one at
+ * http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_PROXY_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_PROXY_H_
+
+#include "LeafInterfaceProxyBase.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+template <typename ... _AttributeExtensions>
+class LeafInterfaceProxy: virtual public LeafInterface, virtual public LeafInterfaceProxyBase, public _AttributeExtensions... {
+ public:
+ LeafInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate);
+ ~LeafInterfaceProxy();
+
+
+
+ /**
+ * Calls testLeafMethod with synchronous semantics.
+ *
+ * All const parameters are input parameters to this method.
+ * All non-const parameters will be filled with the returned values.
+ * The CallStatus will be filled when the method returns and indicate either
+ * "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
+ * will be set.
+ */
+ virtual void testLeafMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString);
+ /**
+ * Calls testLeafMethod with asynchronous semantics.
+ *
+ * The provided callback will be called when the reply to this call arrives or
+ * an error occurs during the call. The CallStatus will indicate either "SUCCESS"
+ * or which type of error has occurred. In case of any error, ONLY the CallStatus
+ * will have a defined value.
+ * The std::future returned by this method will be fulfilled at arrival of the reply.
+ * It will provide the same value for CallStatus as will be handed to the callback.
+ */
+ virtual std::future<CommonAPI::CallStatus> testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback);
+
+
+ /**
+ * Returns the CommonAPI address of the remote partner this proxy communicates with.
+ */
+ virtual std::string getAddress() const;
+
+ /**
+ * Returns the domain of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getDomain() const;
+
+ /**
+ * Returns the service ID of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getServiceId() const;
+
+ /**
+ * Returns the instance ID of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getInstanceId() const;
+
+ /**
+ * Returns true if the remote partner for this proxy is currently known to be available.
+ */
+ virtual bool isAvailable() const;
+
+ /**
+ * Returns true if the remote partner for this proxy is available.
+ */
+ virtual bool isAvailableBlocking() const;
+
+ /**
+ * Returns the wrapper class that is used to (de-)register for notifications about
+ * the availability of the remote partner of this proxy.
+ */
+ virtual CommonAPI::ProxyStatusEvent& getProxyStatusEvent();
+
+ /**
+ * Returns the wrapper class that is used to access version information of the remote
+ * partner of this proxy.
+ */
+ virtual CommonAPI::InterfaceVersionAttribute& getInterfaceVersionAttribute();
+
+ private:
+ std::shared_ptr<LeafInterfaceProxyBase> delegate_;
+};
+
+
+//
+// LeafInterfaceProxy Implementation
+//
+template <typename ... _AttributeExtensions>
+LeafInterfaceProxy<_AttributeExtensions...>::LeafInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate):
+ delegate_(std::dynamic_pointer_cast<LeafInterfaceProxyBase>(delegate)),
+ _AttributeExtensions(*(std::dynamic_pointer_cast<LeafInterfaceProxyBase>(delegate)))... {
+}
+
+template <typename ... _AttributeExtensions>
+LeafInterfaceProxy<_AttributeExtensions...>::~LeafInterfaceProxy() {
+}
+
+template <typename ... _AttributeExtensions>
+void LeafInterfaceProxy<_AttributeExtensions...>::testLeafMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString) {
+ delegate_->testLeafMethod(inInt, inString, callStatus, methodError, outInt, outString);
+}
+
+template <typename ... _AttributeExtensions>
+std::future<CommonAPI::CallStatus> LeafInterfaceProxy<_AttributeExtensions...>::testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback) {
+ return delegate_->testLeafMethodAsync(inInt, inString, callback);
+}
+
+template <typename ... _AttributeExtensions>
+std::string LeafInterfaceProxy<_AttributeExtensions...>::getAddress() const {
+ return delegate_->getAddress();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& LeafInterfaceProxy<_AttributeExtensions...>::getDomain() const {
+ return delegate_->getDomain();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& LeafInterfaceProxy<_AttributeExtensions...>::getServiceId() const {
+ return delegate_->getServiceId();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& LeafInterfaceProxy<_AttributeExtensions...>::getInstanceId() const {
+ return delegate_->getInstanceId();
+}
+
+template <typename ... _AttributeExtensions>
+bool LeafInterfaceProxy<_AttributeExtensions...>::isAvailable() const {
+ return delegate_->isAvailable();
+}
+
+template <typename ... _AttributeExtensions>
+bool LeafInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const {
+ return delegate_->isAvailableBlocking();
+}
+
+template <typename ... _AttributeExtensions>
+CommonAPI::ProxyStatusEvent& LeafInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() {
+ return delegate_->getProxyStatusEvent();
+}
+
+template <typename ... _AttributeExtensions>
+CommonAPI::InterfaceVersionAttribute& LeafInterfaceProxy<_AttributeExtensions...>::getInterfaceVersionAttribute() {
+ return delegate_->getInterfaceVersionAttribute();
+}
+
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_PROXY_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceProxyBase.h b/src/test/commonapi/tests/managed/LeafInterfaceProxyBase.h
new file mode 100644
index 0000000..a4eb7cb
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceProxyBase.h
@@ -0,0 +1,47 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+ * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+ * If a copy of the MPL was not distributed with this file, You can obtain one at
+ * http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_PROXY_BASE_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_PROXY_BASE_H_
+
+#include "LeafInterface.h"
+
+
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Proxy.h>
+#include <functional>
+#include <future>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class LeafInterfaceProxyBase: virtual public CommonAPI::Proxy {
+ public:
+
+ typedef std::function<void(const CommonAPI::CallStatus&, const LeafInterface::testLeafMethodError&, const int32_t&, const std::string&)> TestLeafMethodAsyncCallback;
+
+
+
+ virtual void testLeafMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString) = 0;
+ virtual std::future<CommonAPI::CallStatus> testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback) = 0;
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_PROXY_BASE_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceStub.h b/src/test/commonapi/tests/managed/LeafInterfaceStub.h
new file mode 100644
index 0000000..c41ecaa
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceStub.h
@@ -0,0 +1,91 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+ * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+ * If a copy of the MPL was not distributed with this file, You can obtain one at
+ * http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_STUB_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_STUB_H_
+
+
+
+
+#include "LeafInterface.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Stub.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+/**
+ * Receives messages from remote and handles all dispatching of deserialized calls
+ * to a stub for the service LeafInterface. Also provides means to send broadcasts
+ * and attribute-changed-notifications of observable attributes as defined by this service.
+ * An application developer should not need to bother with this class.
+ */
+class LeafInterfaceStubAdapter: virtual public CommonAPI::StubAdapter, public LeafInterface {
+ public:
+
+
+
+ virtual void deactivateManagedInstances() = 0;
+
+protected:
+ /**
+ * Defines properties for storing the ClientIds of clients / proxies that have
+ * subscribed to the selective broadcasts
+ */
+};
+
+
+/**
+ * Defines the necessary callbacks to handle remote set events related to the attributes
+ * defined in the IDL description for LeafInterface.
+ * For each attribute two callbacks are defined:
+ * - a verification callback that allows to verify the requested value and to prevent setting
+ * e.g. an invalid value ("onRemoteSet<AttributeName>").
+ * - an action callback to do local work after the attribute value has been changed
+ * ("onRemote<AttributeName>Changed").
+ *
+ * This class and the one below are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class LeafInterfaceStubRemoteEvent {
+ public:
+ virtual ~LeafInterfaceStubRemoteEvent() { }
+
+};
+
+
+/**
+ * Defines the interface that must be implemented by any class that should provide
+ * the service LeafInterface to remote clients.
+ * This class and the one above are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class LeafInterfaceStub : public CommonAPI::Stub<LeafInterfaceStubAdapter , LeafInterfaceStubRemoteEvent> {
+ public:
+ virtual ~LeafInterfaceStub() { }
+
+
+ /// This is the method that will be called on remote calls on the method testLeafMethod.
+ virtual void testLeafMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString) = 0;
+
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_STUB_H_
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceStubDefault.cpp b/src/test/commonapi/tests/managed/LeafInterfaceStubDefault.cpp
new file mode 100644
index 0000000..2ce07be
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceStubDefault.cpp
@@ -0,0 +1,43 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+ * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+ * If a copy of the MPL was not distributed with this file, You can obtain one at
+ * http://mozilla.org/MPL/2.0/.
+*/
+#include <commonapi/tests/managed/LeafInterfaceStubDefault.h>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+LeafInterfaceStubDefault::LeafInterfaceStubDefault():
+ remoteEventHandler_(this) {
+}
+
+LeafInterfaceStubRemoteEvent* LeafInterfaceStubDefault::initStubAdapter(const std::shared_ptr<LeafInterfaceStubAdapter>& stubAdapter) {
+ stubAdapter_ = stubAdapter;
+ return &remoteEventHandler_;
+}
+
+
+void LeafInterfaceStubDefault::testLeafMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString) {
+ // Call old style methods in default
+ testLeafMethod(inInt, inString, methodError, outInt, outString);
+}
+void LeafInterfaceStubDefault::testLeafMethod(int32_t inInt, std::string inString, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString) {
+ // No operation in default
+}
+
+
+
+
+LeafInterfaceStubDefault::RemoteEventHandler::RemoteEventHandler(LeafInterfaceStubDefault* defaultStub):
+ defaultStub_(defaultStub) {
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/LeafInterfaceStubDefault.h b/src/test/commonapi/tests/managed/LeafInterfaceStubDefault.h
new file mode 100644
index 0000000..ee324c3
--- /dev/null
+++ b/src/test/commonapi/tests/managed/LeafInterfaceStubDefault.h
@@ -0,0 +1,63 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+ * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+ * If a copy of the MPL was not distributed with this file, You can obtain one at
+ * http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Leaf_Interface_STUB_DEFAULT_H_
+#define COMMONAPI_TESTS_MANAGED_Leaf_Interface_STUB_DEFAULT_H_
+
+#include <commonapi/tests/managed/LeafInterfaceStub.h>
+#include <sstream>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+/**
+ * Provides a default implementation for LeafInterfaceStubRemoteEvent and
+ * LeafInterfaceStub. Method callbacks have an empty implementation,
+ * remote set calls on attributes will always change the value of the attribute
+ * to the one received.
+ *
+ * Override this stub if you only want to provide a subset of the functionality
+ * that would be defined for this service, and/or if you do not need any non-default
+ * behaviour.
+ */
+class LeafInterfaceStubDefault : public LeafInterfaceStub {
+ public:
+ LeafInterfaceStubDefault();
+
+ LeafInterfaceStubRemoteEvent* initStubAdapter(const std::shared_ptr<LeafInterfaceStubAdapter>& stubAdapter);
+
+
+ virtual void testLeafMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString);
+ virtual void testLeafMethod(int32_t inInt, std::string inString, LeafInterface::testLeafMethodError& methodError, int32_t& outInt, std::string& outString);
+
+
+
+
+ protected:
+ std::shared_ptr<LeafInterfaceStubAdapter> stubAdapter_;
+ private:
+ class RemoteEventHandler: public LeafInterfaceStubRemoteEvent {
+ public:
+ RemoteEventHandler(LeafInterfaceStubDefault* defaultStub);
+
+
+ private:
+ LeafInterfaceStubDefault* defaultStub_;
+ };
+
+ RemoteEventHandler remoteEventHandler_;
+
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Leaf_Interface_STUB_DEFAULT_H_
diff --git a/src/test/commonapi/tests/managed/RootInterface.h b/src/test/commonapi/tests/managed/RootInterface.h
new file mode 100644
index 0000000..398e60c
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterface.h
@@ -0,0 +1,91 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_H_
+
+
+#include <set>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/InputStream.h>
+#include <CommonAPI/OutputStream.h>
+#include <CommonAPI/types.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class RootInterface {
+ public:
+ virtual ~RootInterface() { }
+
+ static inline const char* getInterfaceId();
+ static inline CommonAPI::Version getInterfaceVersion();
+ enum class testRootMethodError: int32_t {
+ OK,
+ NOTOK
+ };
+
+ // Definition of a comparator still is necessary for GCC 4.4.1, topic is fixed since 4.5.1
+ struct testRootMethodErrorComparator;
+};
+
+const char* RootInterface::getInterfaceId() {
+ static const char* interfaceId = "commonapi.tests.managed.RootInterface";
+ return interfaceId;
+}
+
+CommonAPI::Version RootInterface::getInterfaceVersion() {
+ return CommonAPI::Version(1, 0);
+}
+
+inline CommonAPI::InputStream& operator>>(CommonAPI::InputStream& inputStream, RootInterface::testRootMethodError& enumValue) {
+ return inputStream.readEnumValue<int32_t>(enumValue);
+}
+
+inline CommonAPI::OutputStream& operator<<(CommonAPI::OutputStream& outputStream, const RootInterface::testRootMethodError& enumValue) {
+ return outputStream.writeEnumValue(static_cast<int32_t>(enumValue));
+}
+
+struct RootInterface::testRootMethodErrorComparator {
+ inline bool operator()(const testRootMethodError& lhs, const testRootMethodError& rhs) const {
+ return static_cast<int32_t>(lhs) < static_cast<int32_t>(rhs);
+ }
+};
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+namespace CommonAPI {
+
+}
+
+
+namespace std {
+ //hashes for types
+
+ //hashes for error types
+ //Hash for testRootMethodError
+ template<>
+ struct hash<commonapi::tests::managed::RootInterface::testRootMethodError> {
+ inline size_t operator()(const commonapi::tests::managed::RootInterface::testRootMethodError& testRootMethodError) const {
+ return static_cast<int32_t>(testRootMethodError);
+ }
+ };
+}
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_H_
diff --git a/src/test/commonapi/tests/managed/RootInterfaceDBusProxy.cpp b/src/test/commonapi/tests/managed/RootInterfaceDBusProxy.cpp
new file mode 100644
index 0000000..4d74952
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceDBusProxy.cpp
@@ -0,0 +1,82 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "RootInterfaceDBusProxy.h"
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+std::shared_ptr<CommonAPI::DBus::DBusProxy> createRootInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection) {
+ return std::make_shared<RootInterfaceDBusProxy>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
+}
+
+__attribute__((constructor)) void registerRootInterfaceDBusProxy(void) {
+ CommonAPI::DBus::DBusFactory::registerProxyFactoryMethod(RootInterface::getInterfaceId(),
+ &createRootInterfaceDBusProxy);
+}
+
+RootInterfaceDBusProxy::RootInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection):
+ CommonAPI::DBus::DBusProxy(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
+, proxyManagerLeafInterface_(*this, "commonapi.tests.managed.LeafInterface", factory),
+ proxyManagerBranchInterface_(*this, "commonapi.tests.managed.BranchInterface", factory)
+ {
+ }
+
+
+
+void RootInterfaceDBusProxy::testRootMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) {
+ CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<int32_t, std::string>,
+ CommonAPI::DBus::DBusSerializableArguments<RootInterface::testRootMethodError, int32_t, std::string> >::callMethodWithReply(
+ *this,
+ "testRootMethod",
+ "is",
+ inInt, inString,
+ callStatus,
+ methodError
+ , outInt, outString);
+}
+std::future<CommonAPI::CallStatus> RootInterfaceDBusProxy::testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback) {
+ return CommonAPI::DBus::DBusProxyHelper<CommonAPI::DBus::DBusSerializableArguments<int32_t, std::string>,
+ CommonAPI::DBus::DBusSerializableArguments<RootInterface::testRootMethodError, int32_t, std::string> >::callMethodAsync(
+ *this,
+ "testRootMethod",
+ "is",
+ inInt, inString,
+ std::move(callback));
+}
+
+CommonAPI::ProxyManager& RootInterfaceDBusProxy::getProxyManagerLeafInterface() {
+ return proxyManagerLeafInterface_;
+}
+CommonAPI::ProxyManager& RootInterfaceDBusProxy::getProxyManagerBranchInterface() {
+ return proxyManagerBranchInterface_;
+}
+
+
+void RootInterfaceDBusProxy::getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const {
+ ownVersionMajor = 1;
+ ownVersionMinor = 0;
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/RootInterfaceDBusProxy.h b/src/test/commonapi/tests/managed/RootInterfaceDBusProxy.h
new file mode 100644
index 0000000..5aaf63f
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceDBusProxy.h
@@ -0,0 +1,66 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_DBUS_PROXY_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_DBUS_PROXY_H_
+
+#include <commonapi/tests/managed/RootInterfaceProxyBase.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusProxy.h>
+#include <CommonAPI/DBus/DBusProxyManager.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+#include <string>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class RootInterfaceDBusProxy: virtual public RootInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy {
+ public:
+ RootInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection);
+
+ virtual ~RootInterfaceDBusProxy() { }
+
+
+
+ virtual void testRootMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString);
+ virtual std::future<CommonAPI::CallStatus> testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback);
+
+ virtual CommonAPI::ProxyManager& getProxyManagerLeafInterface();
+ virtual CommonAPI::ProxyManager& getProxyManagerBranchInterface();
+
+ virtual void getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const;
+
+ private:
+
+
+ CommonAPI::DBus::DBusProxyManager proxyManagerLeafInterface_;
+ CommonAPI::DBus::DBusProxyManager proxyManagerBranchInterface_;
+};
+
+
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_DBUS_PROXY_H_
diff --git a/src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.cpp b/src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.cpp
new file mode 100644
index 0000000..1a3a6dc
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.cpp
@@ -0,0 +1,203 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "RootInterfaceDBusStubAdapter.h"
+#include <commonapi/tests/managed/RootInterface.h>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> createRootInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stubBase) {
+ return std::make_shared<RootInterfaceDBusStubAdapter>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase);
+}
+
+__attribute__((constructor)) void registerRootInterfaceDBusStubAdapter(void) {
+ CommonAPI::DBus::DBusFactory::registerAdapterFactoryMethod(RootInterface::getInterfaceId(),
+ &createRootInterfaceDBusStubAdapter);
+}
+
+RootInterfaceDBusStubAdapter::RootInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub):
+ RootInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath,
+ dbusConnection, std::dynamic_pointer_cast<RootInterfaceStub>(stub),
+ new CommonAPI::DBus::DBusObjectManagerStub(dbusObjectPath, dbusConnection))
+ {
+}
+
+RootInterfaceDBusStubAdapter::~RootInterfaceDBusStubAdapter() {
+ deactivateManagedInstances();
+ deinit();
+ stub_.reset();
+}
+
+void RootInterfaceDBusStubAdapter::deactivateManagedInstances() {
+ for(std::set<std::string>::iterator iter = registeredLeafInterfaceInstances.begin();
+ iter != registeredLeafInterfaceInstances.end(); ++iter) {
+ deregisterManagedStubLeafInterface(*iter);
+ }
+ for(std::set<std::string>::iterator iter = registeredBranchInterfaceInstances.begin();
+ iter != registeredBranchInterfaceInstances.end(); ++iter) {
+ deregisterManagedStubBranchInterface(*iter);
+ }
+}
+
+const char* RootInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const {
+ static const char* introspectionData =
+ "<method name=\"testRootMethod\">\n"
+ "<arg name=\"inInt\" type=\"i\" direction=\"in\" />\n"
+ "<arg name=\"inString\" type=\"s\" direction=\"in\" />\n"
+ "<arg name=\"methodError\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"outInt\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"outString\" type=\"s\" direction=\"out\" />\n"
+ "</method>\n"
+ ;
+ return introspectionData;
+}
+
+
+
+static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher<
+ RootInterfaceStub,
+ std::tuple<int32_t, std::string>,
+ std::tuple<RootInterface::testRootMethodError, int32_t, std::string>
+ > testRootMethodStubDispatcher(&RootInterfaceStub::testRootMethod, "iis");
+
+
+
+const RootInterfaceDBusStubAdapter::StubDispatcherTable& RootInterfaceDBusStubAdapter::getStubDispatcherTable() {
+ static const RootInterfaceDBusStubAdapter::StubDispatcherTable stubDispatcherTable = {
+ { { "testRootMethod", "is" }, &commonapi::tests::managed::testRootMethodStubDispatcher }
+ };
+ return stubDispatcherTable;
+}
+
+
+bool RootInterfaceDBusStubAdapter::registerManagedStubLeafInterface(std::shared_ptr<LeafInterfaceStub> stub, const std::string& instance) {
+ if (registeredLeafInterfaceInstances.find(instance) == registeredLeafInterfaceInstances.end()) {
+ std::string commonApiAddress = "local:commonapi.tests.managed.LeafInterface:" + instance;
+
+ std::string interfaceName;
+ std::string connectionName;
+ std::string objectPath;
+
+ CommonAPI::DBus::DBusAddressTranslator::getInstance().searchForDBusAddress(
+ commonApiAddress,
+ interfaceName,
+ connectionName,
+ objectPath);
+
+ if (objectPath.compare(0, dbusObjectPath_.length(), dbusObjectPath_) == 0) {
+ auto dbusStubAdapter = factory_->createDBusStubAdapter(stub, "commonapi.tests.managed.LeafInterface",
+ instance, "commonapi.tests.managed.LeafInterface", "local");
+ bool ok = CommonAPI::DBus::DBusServicePublisher::getInstance()->registerManagedService(dbusStubAdapter);
+ if (ok) {
+ bool isServiceExportSuccessful = managerStub->exportDBusStubAdapter(dbusStubAdapter.get());
+ if (isServiceExportSuccessful) {
+ registeredLeafInterfaceInstances.insert(instance);
+ return true;
+ } else {
+ const bool isManagedDeregistrationSuccessful =
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterManagedService(
+ commonApiAddress);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool RootInterfaceDBusStubAdapter::deregisterManagedStubLeafInterface(const std::string& instance) {
+ std::string commonApiAddress = "local:commonapi.tests.managed.LeafInterface:" + instance;
+ if (registeredLeafInterfaceInstances.find(instance) != registeredLeafInterfaceInstances.end()) {
+ std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> adapter =
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->getRegisteredService(commonApiAddress);
+ if (adapter != nullptr) {
+ managerStub->unexportDBusStubAdapter(adapter.get());
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterManagedService(commonApiAddress);
+ registeredLeafInterfaceInstances.erase(instance);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::set<std::string>& RootInterfaceDBusStubAdapter::getLeafInterfaceInstances() {
+ return registeredLeafInterfaceInstances;
+}
+
+bool RootInterfaceDBusStubAdapter::registerManagedStubBranchInterface(std::shared_ptr<BranchInterfaceStub> stub, const std::string& instance) {
+ if (registeredBranchInterfaceInstances.find(instance) == registeredBranchInterfaceInstances.end()) {
+ std::string commonApiAddress = "local:commonapi.tests.managed.BranchInterface:" + instance;
+
+ std::string interfaceName;
+ std::string connectionName;
+ std::string objectPath;
+
+ CommonAPI::DBus::DBusAddressTranslator::getInstance().searchForDBusAddress(
+ commonApiAddress,
+ interfaceName,
+ connectionName,
+ objectPath);
+
+ if (objectPath.compare(0, dbusObjectPath_.length(), dbusObjectPath_) == 0) {
+ auto dbusStubAdapter = factory_->createDBusStubAdapter(stub, "commonapi.tests.managed.BranchInterface",
+ instance, "commonapi.tests.managed.BranchInterface", "local");
+ bool ok = CommonAPI::DBus::DBusServicePublisher::getInstance()->registerManagedService(dbusStubAdapter);
+ if (ok) {
+ bool isServiceExportSuccessful = managerStub->exportDBusStubAdapter(dbusStubAdapter.get());
+ if (isServiceExportSuccessful) {
+ registeredBranchInterfaceInstances.insert(instance);
+ return true;
+ } else {
+ const bool isManagedDeregistrationSuccessful =
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterManagedService(
+ commonApiAddress);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool RootInterfaceDBusStubAdapter::deregisterManagedStubBranchInterface(const std::string& instance) {
+ std::string commonApiAddress = "local:commonapi.tests.managed.BranchInterface:" + instance;
+ if (registeredBranchInterfaceInstances.find(instance) != registeredBranchInterfaceInstances.end()) {
+ std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> adapter =
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->getRegisteredService(commonApiAddress);
+ if (adapter != nullptr) {
+ managerStub->unexportDBusStubAdapter(adapter.get());
+ CommonAPI::DBus::DBusServicePublisher::getInstance()->unregisterManagedService(commonApiAddress);
+ registeredBranchInterfaceInstances.erase(instance);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::set<std::string>& RootInterfaceDBusStubAdapter::getBranchInterfaceInstances() {
+ return registeredBranchInterfaceInstances;
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.h b/src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.h
new file mode 100644
index 0000000..a3fd482
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.h
@@ -0,0 +1,70 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_DBUS_STUB_ADAPTER_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_DBUS_STUB_ADAPTER_H_
+
+#include <commonapi/tests/managed/RootInterfaceStub.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusStubAdapterHelper.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+typedef CommonAPI::DBus::DBusStubAdapterHelper<RootInterfaceStub> RootInterfaceDBusStubAdapterHelper;
+
+class RootInterfaceDBusStubAdapter: public RootInterfaceStubAdapter, public RootInterfaceDBusStubAdapterHelper {
+ public:
+ RootInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub);
+
+ ~RootInterfaceDBusStubAdapter();
+
+
+
+ bool registerManagedStubLeafInterface(std::shared_ptr<LeafInterfaceStub>, const std::string&);
+ bool deregisterManagedStubLeafInterface(const std::string&);
+ std::set<std::string>& getLeafInterfaceInstances();
+ bool registerManagedStubBranchInterface(std::shared_ptr<BranchInterfaceStub>, const std::string&);
+ bool deregisterManagedStubBranchInterface(const std::string&);
+ std::set<std::string>& getBranchInterfaceInstances();
+
+ const StubDispatcherTable& getStubDispatcherTable();
+
+ void deactivateManagedInstances();
+
+ protected:
+ virtual const char* getMethodsDBusIntrospectionXmlData() const;
+
+ private:
+ std::set<std::string> registeredLeafInterfaceInstances;
+ std::set<std::string> registeredBranchInterfaceInstances;
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_DBUS_STUB_ADAPTER_H_
diff --git a/src/test/commonapi/tests/managed/RootInterfaceProxy.h b/src/test/commonapi/tests/managed/RootInterfaceProxy.h
new file mode 100644
index 0000000..17f570a
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceProxy.h
@@ -0,0 +1,184 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_PROXY_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_PROXY_H_
+
+#include "RootInterfaceProxyBase.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+template <typename ... _AttributeExtensions>
+class RootInterfaceProxy: virtual public RootInterface, virtual public RootInterfaceProxyBase, public _AttributeExtensions... {
+ public:
+ RootInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate);
+ ~RootInterfaceProxy();
+
+
+
+ /**
+ * Calls testRootMethod with synchronous semantics.
+ *
+ * All const parameters are input parameters to this method.
+ * All non-const parameters will be filled with the returned values.
+ * The CallStatus will be filled when the method returns and indicate either
+ * "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
+ * will be set.
+ */
+ virtual void testRootMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString);
+ /**
+ * Calls testRootMethod with asynchronous semantics.
+ *
+ * The provided callback will be called when the reply to this call arrives or
+ * an error occurs during the call. The CallStatus will indicate either "SUCCESS"
+ * or which type of error has occurred. In case of any error, ONLY the CallStatus
+ * will have a defined value.
+ * The std::future returned by this method will be fulfilled at arrival of the reply.
+ * It will provide the same value for CallStatus as will be handed to the callback.
+ */
+ virtual std::future<CommonAPI::CallStatus> testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback);
+
+ virtual CommonAPI::ProxyManager& getProxyManagerLeafInterface();
+ virtual CommonAPI::ProxyManager& getProxyManagerBranchInterface();
+
+ /**
+ * Returns the CommonAPI address of the remote partner this proxy communicates with.
+ */
+ virtual std::string getAddress() const;
+
+ /**
+ * Returns the domain of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getDomain() const;
+
+ /**
+ * Returns the service ID of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getServiceId() const;
+
+ /**
+ * Returns the instance ID of the remote partner this proxy communicates with.
+ */
+ virtual const std::string& getInstanceId() const;
+
+ /**
+ * Returns true if the remote partner for this proxy is currently known to be available.
+ */
+ virtual bool isAvailable() const;
+
+ /**
+ * Returns true if the remote partner for this proxy is available.
+ */
+ virtual bool isAvailableBlocking() const;
+
+ /**
+ * Returns the wrapper class that is used to (de-)register for notifications about
+ * the availability of the remote partner of this proxy.
+ */
+ virtual CommonAPI::ProxyStatusEvent& getProxyStatusEvent();
+
+ /**
+ * Returns the wrapper class that is used to access version information of the remote
+ * partner of this proxy.
+ */
+ virtual CommonAPI::InterfaceVersionAttribute& getInterfaceVersionAttribute();
+
+ private:
+ std::shared_ptr<RootInterfaceProxyBase> delegate_;
+};
+
+
+//
+// RootInterfaceProxy Implementation
+//
+template <typename ... _AttributeExtensions>
+RootInterfaceProxy<_AttributeExtensions...>::RootInterfaceProxy(std::shared_ptr<CommonAPI::Proxy> delegate):
+ delegate_(std::dynamic_pointer_cast<RootInterfaceProxyBase>(delegate)),
+ _AttributeExtensions(*(std::dynamic_pointer_cast<RootInterfaceProxyBase>(delegate)))... {
+}
+
+template <typename ... _AttributeExtensions>
+RootInterfaceProxy<_AttributeExtensions...>::~RootInterfaceProxy() {
+}
+
+template <typename ... _AttributeExtensions>
+void RootInterfaceProxy<_AttributeExtensions...>::testRootMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) {
+ delegate_->testRootMethod(inInt, inString, callStatus, methodError, outInt, outString);
+}
+
+template <typename ... _AttributeExtensions>
+std::future<CommonAPI::CallStatus> RootInterfaceProxy<_AttributeExtensions...>::testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback) {
+ return delegate_->testRootMethodAsync(inInt, inString, callback);
+}
+
+template <typename ... _AttributeExtensions>
+std::string RootInterfaceProxy<_AttributeExtensions...>::getAddress() const {
+ return delegate_->getAddress();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& RootInterfaceProxy<_AttributeExtensions...>::getDomain() const {
+ return delegate_->getDomain();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& RootInterfaceProxy<_AttributeExtensions...>::getServiceId() const {
+ return delegate_->getServiceId();
+}
+
+template <typename ... _AttributeExtensions>
+const std::string& RootInterfaceProxy<_AttributeExtensions...>::getInstanceId() const {
+ return delegate_->getInstanceId();
+}
+
+template <typename ... _AttributeExtensions>
+bool RootInterfaceProxy<_AttributeExtensions...>::isAvailable() const {
+ return delegate_->isAvailable();
+}
+
+template <typename ... _AttributeExtensions>
+bool RootInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const {
+ return delegate_->isAvailableBlocking();
+}
+
+template <typename ... _AttributeExtensions>
+CommonAPI::ProxyStatusEvent& RootInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() {
+ return delegate_->getProxyStatusEvent();
+}
+
+template <typename ... _AttributeExtensions>
+CommonAPI::InterfaceVersionAttribute& RootInterfaceProxy<_AttributeExtensions...>::getInterfaceVersionAttribute() {
+ return delegate_->getInterfaceVersionAttribute();
+}
+
+
+template <typename ... _AttributeExtensions>
+CommonAPI::ProxyManager& RootInterfaceProxy<_AttributeExtensions...>::getProxyManagerLeafInterface() {
+ return delegate_->getProxyManagerLeafInterface();
+}
+template <typename ... _AttributeExtensions>
+CommonAPI::ProxyManager& RootInterfaceProxy<_AttributeExtensions...>::getProxyManagerBranchInterface() {
+ return delegate_->getProxyManagerBranchInterface();
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_PROXY_H_
diff --git a/src/test/commonapi/tests/managed/RootInterfaceProxyBase.h b/src/test/commonapi/tests/managed/RootInterfaceProxyBase.h
new file mode 100644
index 0000000..ca8a693
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceProxyBase.h
@@ -0,0 +1,52 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_PROXY_BASE_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_PROXY_BASE_H_
+
+#include "RootInterface.h"
+
+
+#include <commonapi/tests/managed/BranchInterfaceStub.h>
+#include <commonapi/tests/managed/LeafInterfaceStub.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/ProxyManager.h>
+#include <CommonAPI/Proxy.h>
+#include <functional>
+#include <future>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+class RootInterfaceProxyBase: virtual public CommonAPI::Proxy {
+ public:
+
+ typedef std::function<void(const CommonAPI::CallStatus&, const RootInterface::testRootMethodError&, const int32_t&, const std::string&)> TestRootMethodAsyncCallback;
+
+
+
+ virtual void testRootMethod(const int32_t& inInt, const std::string& inString, CommonAPI::CallStatus& callStatus, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) = 0;
+ virtual std::future<CommonAPI::CallStatus> testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback) = 0;
+ virtual CommonAPI::ProxyManager& getProxyManagerLeafInterface() = 0;
+ virtual CommonAPI::ProxyManager& getProxyManagerBranchInterface() = 0;
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_PROXY_BASE_H_
diff --git a/src/test/commonapi/tests/managed/RootInterfaceStub.h b/src/test/commonapi/tests/managed/RootInterfaceStub.h
new file mode 100644
index 0000000..c0c5da9
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceStub.h
@@ -0,0 +1,105 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_STUB_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_STUB_H_
+
+
+
+#include <commonapi/tests/managed/BranchInterfaceStub.h>
+#include <commonapi/tests/managed/LeafInterfaceStub.h>
+
+#include "RootInterface.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Stub.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+/**
+ * Receives messages from remote and handles all dispatching of deserialized calls
+ * to a stub for the service RootInterface. Also provides means to send broadcasts
+ * and attribute-changed-notifications of observable attributes as defined by this service.
+ * An application developer should not need to bother with this class.
+ */
+class RootInterfaceStubAdapter: virtual public CommonAPI::StubAdapter, public RootInterface {
+ public:
+
+
+ virtual bool registerManagedStubLeafInterface(std::shared_ptr<LeafInterfaceStub>, const std::string&) = 0;
+ virtual bool deregisterManagedStubLeafInterface(const std::string&) = 0;
+ virtual std::set<std::string>& getLeafInterfaceInstances() = 0;
+ virtual bool registerManagedStubBranchInterface(std::shared_ptr<BranchInterfaceStub>, const std::string&) = 0;
+ virtual bool deregisterManagedStubBranchInterface(const std::string&) = 0;
+ virtual std::set<std::string>& getBranchInterfaceInstances() = 0;
+
+ virtual void deactivateManagedInstances() = 0;
+
+protected:
+ /**
+ * Defines properties for storing the ClientIds of clients / proxies that have
+ * subscribed to the selective broadcasts
+ */
+};
+
+
+/**
+ * Defines the necessary callbacks to handle remote set events related to the attributes
+ * defined in the IDL description for RootInterface.
+ * For each attribute two callbacks are defined:
+ * - a verification callback that allows to verify the requested value and to prevent setting
+ * e.g. an invalid value ("onRemoteSet<AttributeName>").
+ * - an action callback to do local work after the attribute value has been changed
+ * ("onRemote<AttributeName>Changed").
+ *
+ * This class and the one below are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class RootInterfaceStubRemoteEvent {
+ public:
+ virtual ~RootInterfaceStubRemoteEvent() { }
+
+};
+
+
+/**
+ * Defines the interface that must be implemented by any class that should provide
+ * the service RootInterface to remote clients.
+ * This class and the one above are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class RootInterfaceStub : public CommonAPI::Stub<RootInterfaceStubAdapter , RootInterfaceStubRemoteEvent> {
+ public:
+ virtual ~RootInterfaceStub() { }
+
+
+ /// This is the method that will be called on remote calls on the method testRootMethod.
+ virtual void testRootMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) = 0;
+
+ virtual bool registerManagedStubLeafInterface(std::shared_ptr<LeafInterfaceStub>, const std::string&) = 0;
+ virtual bool deregisterManagedStubLeafInterface(const std::string&) = 0;
+ virtual std::set<std::string>& getLeafInterfaceInstances() = 0;
+ virtual bool registerManagedStubBranchInterface(std::shared_ptr<BranchInterfaceStub>, const std::string&) = 0;
+ virtual bool deregisterManagedStubBranchInterface(const std::string&) = 0;
+ virtual std::set<std::string>& getBranchInterfaceInstances() = 0;
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_STUB_H_
diff --git a/src/test/commonapi/tests/managed/RootInterfaceStubDefault.cpp b/src/test/commonapi/tests/managed/RootInterfaceStubDefault.cpp
new file mode 100644
index 0000000..f4d39d1
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceStubDefault.cpp
@@ -0,0 +1,76 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include <commonapi/tests/managed/RootInterfaceStubDefault.h>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+RootInterfaceStubDefault::RootInterfaceStubDefault():
+ autoInstanceCounter_(0),
+ remoteEventHandler_(this) {
+}
+
+RootInterfaceStubRemoteEvent* RootInterfaceStubDefault::initStubAdapter(const std::shared_ptr<RootInterfaceStubAdapter>& stubAdapter) {
+ stubAdapter_ = stubAdapter;
+ return &remoteEventHandler_;
+}
+
+
+void RootInterfaceStubDefault::testRootMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) {
+ // Call old style methods in default
+ testRootMethod(inInt, inString, methodError, outInt, outString);
+}
+void RootInterfaceStubDefault::testRootMethod(int32_t inInt, std::string inString, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) {
+ // No operation in default
+}
+
+
+
+bool RootInterfaceStubDefault::registerManagedStubLeafInterfaceAutoInstance(std::shared_ptr<LeafInterfaceStub> stub) {
+ autoInstanceCounter_++;
+ std::stringstream ss;
+ ss << stubAdapter_->getInstanceId() << "." << autoInstanceCounter_;
+ std::string instance = ss.str();
+ return stubAdapter_->registerManagedStubLeafInterface(stub, instance);
+}
+bool RootInterfaceStubDefault::registerManagedStubLeafInterface(std::shared_ptr<LeafInterfaceStub> stub, const std::string& instance) {
+ return stubAdapter_->registerManagedStubLeafInterface(stub, instance);
+}
+bool RootInterfaceStubDefault::deregisterManagedStubLeafInterface(const std::string& instance) {
+ return stubAdapter_->deregisterManagedStubLeafInterface(instance);
+}
+std::set<std::string>& RootInterfaceStubDefault::getLeafInterfaceInstances() {
+ return stubAdapter_->getLeafInterfaceInstances();
+}
+bool RootInterfaceStubDefault::registerManagedStubBranchInterfaceAutoInstance(std::shared_ptr<BranchInterfaceStub> stub) {
+ autoInstanceCounter_++;
+ std::stringstream ss;
+ ss << stubAdapter_->getInstanceId() << "." << autoInstanceCounter_;
+ std::string instance = ss.str();
+ return stubAdapter_->registerManagedStubBranchInterface(stub, instance);
+}
+bool RootInterfaceStubDefault::registerManagedStubBranchInterface(std::shared_ptr<BranchInterfaceStub> stub, const std::string& instance) {
+ return stubAdapter_->registerManagedStubBranchInterface(stub, instance);
+}
+bool RootInterfaceStubDefault::deregisterManagedStubBranchInterface(const std::string& instance) {
+ return stubAdapter_->deregisterManagedStubBranchInterface(instance);
+}
+std::set<std::string>& RootInterfaceStubDefault::getBranchInterfaceInstances() {
+ return stubAdapter_->getBranchInterfaceInstances();
+}
+
+RootInterfaceStubDefault::RemoteEventHandler::RemoteEventHandler(RootInterfaceStubDefault* defaultStub):
+ defaultStub_(defaultStub) {
+}
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
diff --git a/src/test/commonapi/tests/managed/RootInterfaceStubDefault.h b/src/test/commonapi/tests/managed/RootInterfaceStubDefault.h
new file mode 100644
index 0000000..c2e0620
--- /dev/null
+++ b/src/test/commonapi/tests/managed/RootInterfaceStubDefault.h
@@ -0,0 +1,72 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef COMMONAPI_TESTS_MANAGED_Root_Interface_STUB_DEFAULT_H_
+#define COMMONAPI_TESTS_MANAGED_Root_Interface_STUB_DEFAULT_H_
+
+#include <commonapi/tests/managed/RootInterfaceStub.h>
+#include <sstream>
+
+namespace commonapi {
+namespace tests {
+namespace managed {
+
+/**
+ * Provides a default implementation for RootInterfaceStubRemoteEvent and
+ * RootInterfaceStub. Method callbacks have an empty implementation,
+ * remote set calls on attributes will always change the value of the attribute
+ * to the one received.
+ *
+ * Override this stub if you only want to provide a subset of the functionality
+ * that would be defined for this service, and/or if you do not need any non-default
+ * behaviour.
+ */
+class RootInterfaceStubDefault : public RootInterfaceStub {
+ public:
+ RootInterfaceStubDefault();
+
+ RootInterfaceStubRemoteEvent* initStubAdapter(const std::shared_ptr<RootInterfaceStubAdapter>& stubAdapter);
+
+
+ virtual void testRootMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t inInt, std::string inString, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString);
+ virtual void testRootMethod(int32_t inInt, std::string inString, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString);
+
+
+
+ bool registerManagedStubLeafInterfaceAutoInstance(std::shared_ptr<LeafInterfaceStub>);
+ bool registerManagedStubLeafInterface(std::shared_ptr<LeafInterfaceStub>, const std::string&);
+ bool deregisterManagedStubLeafInterface(const std::string&);
+ std::set<std::string>& getLeafInterfaceInstances();
+ bool registerManagedStubBranchInterfaceAutoInstance(std::shared_ptr<BranchInterfaceStub>);
+ bool registerManagedStubBranchInterface(std::shared_ptr<BranchInterfaceStub>, const std::string&);
+ bool deregisterManagedStubBranchInterface(const std::string&);
+ std::set<std::string>& getBranchInterfaceInstances();
+
+ protected:
+ std::shared_ptr<RootInterfaceStubAdapter> stubAdapter_;
+ private:
+ class RemoteEventHandler: public RootInterfaceStubRemoteEvent {
+ public:
+ RemoteEventHandler(RootInterfaceStubDefault* defaultStub);
+
+
+ private:
+ RootInterfaceStubDefault* defaultStub_;
+ };
+
+ RemoteEventHandler remoteEventHandler_;
+ uint32_t autoInstanceCounter_;
+
+};
+
+} // namespace managed
+} // namespace tests
+} // namespace commonapi
+
+#endif // COMMONAPI_TESTS_MANAGED_Root_Interface_STUB_DEFAULT_H_
diff --git a/src/test/fake/legacy/service/LegacyInterface.h b/src/test/fake/legacy/service/LegacyInterface.h
index 6a604d5..c0c10e5 100644
--- a/src/test/fake/legacy/service/LegacyInterface.h
+++ b/src/test/fake/legacy/service/LegacyInterface.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_H_
#define FAKE_LEGACY_SERVICE_Legacy_Interface_H_
diff --git a/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.cpp b/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.cpp
index 7006015..8b73047 100644
--- a/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.cpp
+++ b/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.cpp
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#include "LegacyInterfaceDBusProxy.h"
@@ -12,12 +14,13 @@ namespace legacy {
namespace service {
std::shared_ptr<CommonAPI::DBus::DBusProxy> createLegacyInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
const std::string& objectPath,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection) {
- return std::make_shared<LegacyInterfaceDBusProxy>(commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
+ return std::make_shared<LegacyInterfaceDBusProxy>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection);
}
__attribute__((constructor)) void registerLegacyInterfaceDBusProxy(void) {
@@ -26,14 +29,15 @@ __attribute__((constructor)) void registerLegacyInterfaceDBusProxy(void) {
}
LegacyInterfaceDBusProxy::LegacyInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
const std::string& objectPath,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyconnection):
- CommonAPI::DBus::DBusProxy(commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
- {
-}
+ CommonAPI::DBus::DBusProxy(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection)
+ {
+ }
@@ -91,6 +95,8 @@ std::future<CommonAPI::CallStatus> LegacyInterfaceDBusProxy::finishAsync(FinishA
std::move(callback));
}
+
+
void LegacyInterfaceDBusProxy::getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const {
ownVersionMajor = 1;
ownVersionMinor = 0;
diff --git a/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.h b/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.h
index 05782d8..82af3b0 100644
--- a/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.h
+++ b/src/test/fake/legacy/service/LegacyInterfaceDBusProxy.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_DBUS_PROXY_H_
#define FAKE_LEGACY_SERVICE_Legacy_Interface_DBUS_PROXY_H_
@@ -28,6 +30,7 @@ namespace service {
class LegacyInterfaceDBusProxy: virtual public LegacyInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy {
public:
LegacyInterfaceDBusProxy(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
const std::string& commonApiAddress,
const std::string& interfaceName,
const std::string& busName,
@@ -38,22 +41,23 @@ class LegacyInterfaceDBusProxy: virtual public LegacyInterfaceProxyBase, virtual
-
virtual void TestMethod(const int32_t& input, CommonAPI::CallStatus& callStatus, int32_t& val1, int32_t& val2);
virtual std::future<CommonAPI::CallStatus> TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback);
-
virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier);
virtual std::future<CommonAPI::CallStatus> OtherTestMethodAsync(OtherTestMethodAsyncCallback callback);
-
virtual void finish(CommonAPI::CallStatus& callStatus);
virtual std::future<CommonAPI::CallStatus> finishAsync(FinishAsyncCallback callback);
+
virtual void getOwnVersion(uint16_t& ownVersionMajor, uint16_t& ownVersionMinor) const;
private:
+
};
+
+
} // namespace service
} // namespace legacy
} // namespace fake
diff --git a/src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.cpp b/src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.cpp
new file mode 100644
index 0000000..c1556e6
--- /dev/null
+++ b/src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.cpp
@@ -0,0 +1,105 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include "LegacyInterfaceDBusStubAdapter.h"
+#include <fake/legacy/service/LegacyInterface.h>
+
+namespace fake {
+namespace legacy {
+namespace service {
+
+std::shared_ptr<CommonAPI::DBus::DBusStubAdapter> createLegacyInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& interfaceName,
+ const std::string& busName,
+ const std::string& objectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusProxyConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stubBase) {
+ return std::make_shared<LegacyInterfaceDBusStubAdapter>(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase);
+}
+
+__attribute__((constructor)) void registerLegacyInterfaceDBusStubAdapter(void) {
+ CommonAPI::DBus::DBusFactory::registerAdapterFactoryMethod(LegacyInterface::getInterfaceId(),
+ &createLegacyInterfaceDBusStubAdapter);
+}
+
+LegacyInterfaceDBusStubAdapter::LegacyInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub):
+ LegacyInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath,
+ dbusConnection, std::dynamic_pointer_cast<LegacyInterfaceStub>(stub),
+ NULL)
+ {
+}
+
+LegacyInterfaceDBusStubAdapter::~LegacyInterfaceDBusStubAdapter() {
+ deactivateManagedInstances();
+ deinit();
+ stub_.reset();
+}
+
+void LegacyInterfaceDBusStubAdapter::deactivateManagedInstances() {
+}
+
+const char* LegacyInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const {
+ static const char* introspectionData =
+ "<method name=\"TestMethod\">\n"
+ "<arg name=\"input\" type=\"i\" direction=\"in\" />\n"
+ "<arg name=\"val1\" type=\"i\" direction=\"out\" />\n"
+ "<arg name=\"val2\" type=\"i\" direction=\"out\" />\n"
+ "</method>\n"
+ "<method name=\"OtherTestMethod\">\n"
+ "<arg name=\"greeting\" type=\"s\" direction=\"out\" />\n"
+ "<arg name=\"identifier\" type=\"i\" direction=\"out\" />\n"
+ "</method>\n"
+ "<method name=\"finish\">\n"
+ "</method>\n"
+ ;
+ return introspectionData;
+}
+
+
+
+static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher<
+ LegacyInterfaceStub,
+ std::tuple<int32_t>,
+ std::tuple<int32_t, int32_t>
+ > testMethodStubDispatcher(&LegacyInterfaceStub::TestMethod, "ii");
+static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher<
+ LegacyInterfaceStub,
+ std::tuple<>,
+ std::tuple<std::string, int32_t>
+ > otherTestMethodStubDispatcher(&LegacyInterfaceStub::OtherTestMethod, "si");
+static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher<
+ LegacyInterfaceStub,
+ std::tuple<>,
+ std::tuple<>
+ > finishStubDispatcher(&LegacyInterfaceStub::finish, "");
+
+
+
+const LegacyInterfaceDBusStubAdapter::StubDispatcherTable& LegacyInterfaceDBusStubAdapter::getStubDispatcherTable() {
+ static const LegacyInterfaceDBusStubAdapter::StubDispatcherTable stubDispatcherTable = {
+ { { "TestMethod", "i" }, &fake::legacy::service::testMethodStubDispatcher },
+ { { "OtherTestMethod", "" }, &fake::legacy::service::otherTestMethodStubDispatcher },
+ { { "finish", "" }, &fake::legacy::service::finishStubDispatcher }
+ };
+ return stubDispatcherTable;
+}
+
+
+} // namespace service
+} // namespace legacy
+} // namespace fake
diff --git a/src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.h b/src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.h
new file mode 100644
index 0000000..85939ea
--- /dev/null
+++ b/src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.h
@@ -0,0 +1,62 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_DBUS_STUB_ADAPTER_H_
+#define FAKE_LEGACY_SERVICE_Legacy_Interface_DBUS_STUB_ADAPTER_H_
+
+#include <fake/legacy/service/LegacyInterfaceStub.h>
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+#include <CommonAPI/DBus/DBusStubAdapterHelper.h>
+#include <CommonAPI/DBus/DBusStubAdapter.h>
+#include <CommonAPI/DBus/DBusFactory.h>
+#include <CommonAPI/DBus/DBusServicePublisher.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace fake {
+namespace legacy {
+namespace service {
+
+typedef CommonAPI::DBus::DBusStubAdapterHelper<LegacyInterfaceStub> LegacyInterfaceDBusStubAdapterHelper;
+
+class LegacyInterfaceDBusStubAdapter: public LegacyInterfaceStubAdapter, public LegacyInterfaceDBusStubAdapterHelper {
+ public:
+ LegacyInterfaceDBusStubAdapter(
+ const std::shared_ptr<CommonAPI::DBus::DBusFactory>& factory,
+ const std::string& commonApiAddress,
+ const std::string& dbusInterfaceName,
+ const std::string& dbusBusName,
+ const std::string& dbusObjectPath,
+ const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection>& dbusConnection,
+ const std::shared_ptr<CommonAPI::StubBase>& stub);
+
+ ~LegacyInterfaceDBusStubAdapter();
+
+
+
+
+ const StubDispatcherTable& getStubDispatcherTable();
+
+ void deactivateManagedInstances();
+
+ protected:
+ virtual const char* getMethodsDBusIntrospectionXmlData() const;
+
+ private:
+};
+
+} // namespace service
+} // namespace legacy
+} // namespace fake
+
+#endif // FAKE_LEGACY_SERVICE_Legacy_Interface_DBUS_STUB_ADAPTER_H_
diff --git a/src/test/fake/legacy/service/LegacyInterfaceProxy.h b/src/test/fake/legacy/service/LegacyInterfaceProxy.h
index 51fff47..c849f58 100644
--- a/src/test/fake/legacy/service/LegacyInterfaceProxy.h
+++ b/src/test/fake/legacy/service/LegacyInterfaceProxy.h
@@ -1,9 +1,11 @@
/*
-* This file was generated by the CommonAPI Generators.
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
*
-* This Source Code Form is subject to the terms of the Mozilla Public
-* License, v. 2.0. If a copy of the MPL was not distributed with this
-* file, You can obtain one at http://mozilla.org/MPL/2.0/.
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
*/
#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_H_
#define FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_H_
@@ -29,16 +31,14 @@ class LegacyInterfaceProxy: virtual public LegacyInterface, virtual public Legac
-
/**
* Calls TestMethod with synchronous semantics.
*
- * All const parameters are input parameters to this method.
- * All non-const parameters will be filled with the returned values.
+ * All const parameters are input parameters to this method.
+ * All non-const parameters will be filled with the returned values.
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
- * Synchronous calls are not supported (will block indefinitely) when mainloop integration is used.
*/
virtual void TestMethod(const int32_t& input, CommonAPI::CallStatus& callStatus, int32_t& val1, int32_t& val2);
/**
@@ -52,15 +52,13 @@ class LegacyInterfaceProxy: virtual public LegacyInterface, virtual public Legac
* It will provide the same value for CallStatus as will be handed to the callback.
*/
virtual std::future<CommonAPI::CallStatus> TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback);
-
/**
* Calls OtherTestMethod with synchronous semantics.
*
- * All non-const parameters will be filled with the returned values.
+ * All non-const parameters will be filled with the returned values.
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
- * Synchronous calls are not supported (will block indefinitely) when mainloop integration is used.
*/
virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier);
/**
@@ -74,14 +72,12 @@ class LegacyInterfaceProxy: virtual public LegacyInterface, virtual public Legac
* It will provide the same value for CallStatus as will be handed to the callback.
*/
virtual std::future<CommonAPI::CallStatus> OtherTestMethodAsync(OtherTestMethodAsyncCallback callback);
-
/**
* Calls finish with synchronous semantics.
*
* The CallStatus will be filled when the method returns and indicate either
* "SUCCESS" or which type of error has occurred. In case of an error, ONLY the CallStatus
* will be set.
- * Synchronous calls are not supported (will block indefinitely) when mainloop integration is used.
*/
virtual void finish(CommonAPI::CallStatus& callStatus);
/**
@@ -95,23 +91,39 @@ class LegacyInterfaceProxy: virtual public LegacyInterface, virtual public Legac
* It will provide the same value for CallStatus as will be handed to the callback.
*/
virtual std::future<CommonAPI::CallStatus> finishAsync(FinishAsyncCallback callback);
+
- /// Returns the CommonAPI address of the remote partner this proxy communicates with.
+ /**
+ * Returns the CommonAPI address of the remote partner this proxy communicates with.
+ */
virtual std::string getAddress() const;
- /// Returns the domain of the remote partner this proxy communicates with.
+ /**
+ * Returns the domain of the remote partner this proxy communicates with.
+ */
virtual const std::string& getDomain() const;
- /// Returns the service ID of the remote partner this proxy communicates with.
+ /**
+ * Returns the service ID of the remote partner this proxy communicates with.
+ */
virtual const std::string& getServiceId() const;
- /// Returns the instance ID of the remote partner this proxy communicates with.
+ /**
+ * Returns the instance ID of the remote partner this proxy communicates with.
+ */
virtual const std::string& getInstanceId() const;
- /// Returns true if the remote partner for this proxy is available.
+ /**
+ * Returns true if the remote partner for this proxy is currently known to be available.
+ */
virtual bool isAvailable() const;
/**
+ * Returns true if the remote partner for this proxy is available.
+ */
+ virtual bool isAvailableBlocking() const;
+
+ /**
* Returns the wrapper class that is used to (de-)register for notifications about
* the availability of the remote partner of this proxy.
*/
@@ -150,7 +162,6 @@ template <typename ... _AttributeExtensions>
std::future<CommonAPI::CallStatus> LegacyInterfaceProxy<_AttributeExtensions...>::TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback) {
return delegate_->TestMethodAsync(input, callback);
}
-
template <typename ... _AttributeExtensions>
void LegacyInterfaceProxy<_AttributeExtensions...>::OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier) {
delegate_->OtherTestMethod(callStatus, greeting, identifier);
@@ -160,7 +171,6 @@ template <typename ... _AttributeExtensions>
std::future<CommonAPI::CallStatus> LegacyInterfaceProxy<_AttributeExtensions...>::OtherTestMethodAsync(OtherTestMethodAsyncCallback callback) {
return delegate_->OtherTestMethodAsync(callback);
}
-
template <typename ... _AttributeExtensions>
void LegacyInterfaceProxy<_AttributeExtensions...>::finish(CommonAPI::CallStatus& callStatus) {
delegate_->finish(callStatus);
@@ -171,7 +181,6 @@ std::future<CommonAPI::CallStatus> LegacyInterfaceProxy<_AttributeExtensions...>
return delegate_->finishAsync(callback);
}
-
template <typename ... _AttributeExtensions>
std::string LegacyInterfaceProxy<_AttributeExtensions...>::getAddress() const {
return delegate_->getAddress();
@@ -198,6 +207,11 @@ bool LegacyInterfaceProxy<_AttributeExtensions...>::isAvailable() const {
}
template <typename ... _AttributeExtensions>
+bool LegacyInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const {
+ return delegate_->isAvailableBlocking();
+}
+
+template <typename ... _AttributeExtensions>
CommonAPI::ProxyStatusEvent& LegacyInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() {
return delegate_->getProxyStatusEvent();
}
@@ -207,10 +221,11 @@ CommonAPI::InterfaceVersionAttribute& LegacyInterfaceProxy<_AttributeExtensions.
return delegate_->getInterfaceVersionAttribute();
}
+
+
} // namespace service
} // namespace legacy
} // namespace fake
-
#endif // FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_H_
diff --git a/src/test/fake/legacy/service/LegacyInterfaceProxyBase.h b/src/test/fake/legacy/service/LegacyInterfaceProxyBase.h
index f6d8467..05a66a9 100644
--- a/src/test/fake/legacy/service/LegacyInterfaceProxyBase.h
+++ b/src/test/fake/legacy/service/LegacyInterfaceProxyBase.h
@@ -1,53 +1,53 @@
- /*
- * This file was generated by the CommonAPI Generators.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- #ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_BASE_H_
- #define FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_BASE_H_
-
- #include "LegacyInterface.h"
-
-
-
- #if !defined (COMMONAPI_INTERNAL_COMPILATION)
- #define COMMONAPI_INTERNAL_COMPILATION
- #endif
-
-
- #include <CommonAPI/Proxy.h>
- #include <functional>
- #include <future>
-
- #undef COMMONAPI_INTERNAL_COMPILATION
-
- namespace fake {
- namespace legacy {
- namespace service {
-
- class LegacyInterfaceProxyBase: virtual public CommonAPI::Proxy {
- public:
- typedef std::function<void(const CommonAPI::CallStatus&, const int32_t&, const int32_t&)> TestMethodAsyncCallback;
- typedef std::function<void(const CommonAPI::CallStatus&, const std::string&, const int32_t&)> OtherTestMethodAsyncCallback;
- typedef std::function<void(const CommonAPI::CallStatus&)> FinishAsyncCallback;
-
-
-
-
- virtual void TestMethod(const int32_t& input, CommonAPI::CallStatus& callStatus, int32_t& val1, int32_t& val2) = 0;
- virtual std::future<CommonAPI::CallStatus> TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback) = 0;
-
- virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier) = 0;
- virtual std::future<CommonAPI::CallStatus> OtherTestMethodAsync(OtherTestMethodAsyncCallback callback) = 0;
-
- virtual void finish(CommonAPI::CallStatus& callStatus) = 0;
- virtual std::future<CommonAPI::CallStatus> finishAsync(FinishAsyncCallback callback) = 0;
- };
-
- } // namespace service
- } // namespace legacy
- } // namespace fake
-
- #endif // FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_BASE_H_
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_BASE_H_
+#define FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_BASE_H_
+
+#include "LegacyInterface.h"
+
+
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Proxy.h>
+#include <functional>
+#include <future>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace fake {
+namespace legacy {
+namespace service {
+
+class LegacyInterfaceProxyBase: virtual public CommonAPI::Proxy {
+ public:
+
+ typedef std::function<void(const CommonAPI::CallStatus&, const int32_t&, const int32_t&)> TestMethodAsyncCallback;
+ typedef std::function<void(const CommonAPI::CallStatus&, const std::string&, const int32_t&)> OtherTestMethodAsyncCallback;
+ typedef std::function<void(const CommonAPI::CallStatus&)> FinishAsyncCallback;
+
+
+
+ virtual void TestMethod(const int32_t& input, CommonAPI::CallStatus& callStatus, int32_t& val1, int32_t& val2) = 0;
+ virtual std::future<CommonAPI::CallStatus> TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback) = 0;
+ virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier) = 0;
+ virtual std::future<CommonAPI::CallStatus> OtherTestMethodAsync(OtherTestMethodAsyncCallback callback) = 0;
+ virtual void finish(CommonAPI::CallStatus& callStatus) = 0;
+ virtual std::future<CommonAPI::CallStatus> finishAsync(FinishAsyncCallback callback) = 0;
+};
+
+} // namespace service
+} // namespace legacy
+} // namespace fake
+
+#endif // FAKE_LEGACY_SERVICE_Legacy_Interface_PROXY_BASE_H_
diff --git a/src/test/fake/legacy/service/LegacyInterfaceStub.h b/src/test/fake/legacy/service/LegacyInterfaceStub.h
new file mode 100644
index 0000000..33eb44e
--- /dev/null
+++ b/src/test/fake/legacy/service/LegacyInterfaceStub.h
@@ -0,0 +1,95 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_STUB_H_
+#define FAKE_LEGACY_SERVICE_Legacy_Interface_STUB_H_
+
+
+
+
+#include "LegacyInterface.h"
+
+#if !defined (COMMONAPI_INTERNAL_COMPILATION)
+#define COMMONAPI_INTERNAL_COMPILATION
+#endif
+
+
+#include <CommonAPI/Stub.h>
+
+#undef COMMONAPI_INTERNAL_COMPILATION
+
+namespace fake {
+namespace legacy {
+namespace service {
+
+/**
+ * Receives messages from remote and handles all dispatching of deserialized calls
+ * to a stub for the service LegacyInterface. Also provides means to send broadcasts
+ * and attribute-changed-notifications of observable attributes as defined by this service.
+ * An application developer should not need to bother with this class.
+ */
+class LegacyInterfaceStubAdapter: virtual public CommonAPI::StubAdapter, public LegacyInterface {
+ public:
+
+
+
+ virtual void deactivateManagedInstances() = 0;
+
+protected:
+ /**
+ * Defines properties for storing the ClientIds of clients / proxies that have
+ * subscribed to the selective broadcasts
+ */
+};
+
+
+/**
+ * Defines the necessary callbacks to handle remote set events related to the attributes
+ * defined in the IDL description for LegacyInterface.
+ * For each attribute two callbacks are defined:
+ * - a verification callback that allows to verify the requested value and to prevent setting
+ * e.g. an invalid value ("onRemoteSet<AttributeName>").
+ * - an action callback to do local work after the attribute value has been changed
+ * ("onRemote<AttributeName>Changed").
+ *
+ * This class and the one below are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class LegacyInterfaceStubRemoteEvent {
+ public:
+ virtual ~LegacyInterfaceStubRemoteEvent() { }
+
+};
+
+
+/**
+ * Defines the interface that must be implemented by any class that should provide
+ * the service LegacyInterface to remote clients.
+ * This class and the one above are the ones an application developer needs to have
+ * a look at if he wants to implement a service.
+ */
+class LegacyInterfaceStub : public CommonAPI::Stub<LegacyInterfaceStubAdapter , LegacyInterfaceStubRemoteEvent> {
+ public:
+ virtual ~LegacyInterfaceStub() { }
+
+
+ /// This is the method that will be called on remote calls on the method TestMethod.
+ virtual void TestMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t input, int32_t& val1, int32_t& val2) = 0;
+ /// This is the method that will be called on remote calls on the method OtherTestMethod.
+ virtual void OtherTestMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, std::string& greeting, int32_t& identifier) = 0;
+ /// This is the method that will be called on remote calls on the method finish.
+ virtual void finish(const std::shared_ptr<CommonAPI::ClientId> clientId) = 0;
+
+};
+
+} // namespace service
+} // namespace legacy
+} // namespace fake
+
+#endif // FAKE_LEGACY_SERVICE_Legacy_Interface_STUB_H_
diff --git a/src/test/fake/legacy/service/LegacyInterfaceStubDefault.cpp b/src/test/fake/legacy/service/LegacyInterfaceStubDefault.cpp
new file mode 100644
index 0000000..4510f8c
--- /dev/null
+++ b/src/test/fake/legacy/service/LegacyInterfaceStubDefault.cpp
@@ -0,0 +1,59 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#include <fake/legacy/service/LegacyInterfaceStubDefault.h>
+
+namespace fake {
+namespace legacy {
+namespace service {
+
+LegacyInterfaceStubDefault::LegacyInterfaceStubDefault():
+ remoteEventHandler_(this) {
+}
+
+LegacyInterfaceStubRemoteEvent* LegacyInterfaceStubDefault::initStubAdapter(const std::shared_ptr<LegacyInterfaceStubAdapter>& stubAdapter) {
+ stubAdapter_ = stubAdapter;
+ return &remoteEventHandler_;
+}
+
+
+void LegacyInterfaceStubDefault::TestMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t input, int32_t& val1, int32_t& val2) {
+ // Call old style methods in default
+ TestMethod(input, val1, val2);
+}
+void LegacyInterfaceStubDefault::TestMethod(int32_t input, int32_t& val1, int32_t& val2) {
+ // No operation in default
+}
+
+void LegacyInterfaceStubDefault::OtherTestMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, std::string& greeting, int32_t& identifier) {
+ // Call old style methods in default
+ OtherTestMethod(, greeting, identifier);
+}
+void LegacyInterfaceStubDefault::OtherTestMethod(, std::string& greeting, int32_t& identifier) {
+ // No operation in default
+}
+
+void LegacyInterfaceStubDefault::finish(const std::shared_ptr<CommonAPI::ClientId> clientId) {
+ // Call old style methods in default
+ finish();
+}
+void LegacyInterfaceStubDefault::finish() {
+ // No operation in default
+}
+
+
+
+
+LegacyInterfaceStubDefault::RemoteEventHandler::RemoteEventHandler(LegacyInterfaceStubDefault* defaultStub):
+ defaultStub_(defaultStub) {
+}
+
+} // namespace service
+} // namespace legacy
+} // namespace fake
diff --git a/src/test/fake/legacy/service/LegacyInterfaceStubDefault.h b/src/test/fake/legacy/service/LegacyInterfaceStubDefault.h
new file mode 100644
index 0000000..17af359
--- /dev/null
+++ b/src/test/fake/legacy/service/LegacyInterfaceStubDefault.h
@@ -0,0 +1,69 @@
+/*
+* This file was generated by the CommonAPI Generators.
+* Used org.genivi.commonapi.core 2.1.0.qualifier.
+* Used org.franca.core 0.8.9.201308271211.
+*
+* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+* If a copy of the MPL was not distributed with this file, You can obtain one at
+* http://mozilla.org/MPL/2.0/.
+*/
+#ifndef FAKE_LEGACY_SERVICE_Legacy_Interface_STUB_DEFAULT_H_
+#define FAKE_LEGACY_SERVICE_Legacy_Interface_STUB_DEFAULT_H_
+
+#include <fake/legacy/service/LegacyInterfaceStub.h>
+#include <sstream>
+
+namespace fake {
+namespace legacy {
+namespace service {
+
+/**
+ * Provides a default implementation for LegacyInterfaceStubRemoteEvent and
+ * LegacyInterfaceStub. Method callbacks have an empty implementation,
+ * remote set calls on attributes will always change the value of the attribute
+ * to the one received.
+ *
+ * Override this stub if you only want to provide a subset of the functionality
+ * that would be defined for this service, and/or if you do not need any non-default
+ * behaviour.
+ */
+class LegacyInterfaceStubDefault : public LegacyInterfaceStub {
+ public:
+ LegacyInterfaceStubDefault();
+
+ LegacyInterfaceStubRemoteEvent* initStubAdapter(const std::shared_ptr<LegacyInterfaceStubAdapter>& stubAdapter);
+
+
+ virtual void TestMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, int32_t input, int32_t& val1, int32_t& val2);
+ virtual void TestMethod(int32_t input, int32_t& val1, int32_t& val2);
+
+ virtual void OtherTestMethod(const std::shared_ptr<CommonAPI::ClientId> clientId, std::string& greeting, int32_t& identifier);
+ virtual void OtherTestMethod(, std::string& greeting, int32_t& identifier);
+
+ virtual void finish(const std::shared_ptr<CommonAPI::ClientId> clientId);
+ virtual void finish();
+
+
+
+
+ protected:
+ std::shared_ptr<LegacyInterfaceStubAdapter> stubAdapter_;
+ private:
+ class RemoteEventHandler: public LegacyInterfaceStubRemoteEvent {
+ public:
+ RemoteEventHandler(LegacyInterfaceStubDefault* defaultStub);
+
+
+ private:
+ LegacyInterfaceStubDefault* defaultStub_;
+ };
+
+ RemoteEventHandler remoteEventHandler_;
+
+};
+
+} // namespace service
+} // namespace legacy
+} // namespace fake
+
+#endif // FAKE_LEGACY_SERVICE_Legacy_Interface_STUB_DEFAULT_H_
diff --git a/src/test/fakeLegacyService/fakeLegacyService.fidl b/src/test/fakeLegacyService.fidl
index 6aaa8a8..6aaa8a8 100644
--- a/src/test/fakeLegacyService/fakeLegacyService.fidl
+++ b/src/test/fakeLegacyService.fidl
diff --git a/src/test/leaf.fidl b/src/test/leaf.fidl
new file mode 100644
index 0000000..3cb907e
--- /dev/null
+++ b/src/test/leaf.fidl
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+package commonapi.tests.managed
+
+interface LeafInterface {
+ version { major 1 minor 0 }
+
+
+ method testLeafMethod {
+ in {
+ Int32 inInt
+ String inString
+ }
+ out {
+ Int32 outInt
+ String outString
+ }
+ error {
+ OK
+ NOTOK
+ }
+ }
+
+}
+
diff --git a/src/test/root.fidl b/src/test/root.fidl
new file mode 100644
index 0000000..1183953
--- /dev/null
+++ b/src/test/root.fidl
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+package commonapi.tests.managed
+
+import commonapi.tests.managed.* from "leaf.fidl"
+
+
+interface BranchInterface {
+ version { major 1 minor 0 }
+
+
+ method testBranchMethod {
+ in {
+ Int32 inInt
+ String inString
+ }
+ out {
+ Int32 outInt
+ String outString
+ }
+ error {
+ OK
+ NOTOK
+ }
+ }
+
+}
+
+
+
+interface RootInterface manages LeafInterface, BranchInterface {
+ version { major 1 minor 0 }
+
+ method testRootMethod {
+ in {
+ Int32 inInt
+ String inString
+ }
+ out {
+ Int32 outInt
+ String outString
+ }
+ error {
+ OK
+ NOTOK
+ }
+ }
+
+}
+