From cdddda28ea0c93c73dacafa17bf03f7f9bb4572a Mon Sep 17 00:00:00 2001 From: Johannes Schanda Date: Tue, 17 Sep 2013 13:58:27 +0200 Subject: Add managed services - 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 --- .gitignore | 7 +- Makefile.am | 48 +- README | 2 + src/CommonAPI/DBus/DBusAddressTranslator.cpp | 20 +- src/CommonAPI/DBus/DBusAddressTranslator.h | 9 + src/CommonAPI/DBus/DBusConnection.cpp | 310 +- src/CommonAPI/DBus/DBusConnection.h | 20 + src/CommonAPI/DBus/DBusDaemonProxy.cpp | 26 + src/CommonAPI/DBus/DBusDaemonProxy.h | 45 +- src/CommonAPI/DBus/DBusFactory.cpp | 60 +- src/CommonAPI/DBus/DBusFactory.h | 29 +- .../DBusInstanceAvailabilityStatusChangedEvent.h | 135 + src/CommonAPI/DBus/DBusInterfaceHandler.h | 30 + src/CommonAPI/DBus/DBusObjectManager.cpp | 129 +- src/CommonAPI/DBus/DBusObjectManager.h | 37 +- src/CommonAPI/DBus/DBusObjectManagerStub.cpp | 283 + src/CommonAPI/DBus/DBusObjectManagerStub.h | 149 + src/CommonAPI/DBus/DBusProxy.cpp | 14 +- src/CommonAPI/DBus/DBusProxy.h | 11 +- src/CommonAPI/DBus/DBusProxyBase.h | 3 + src/CommonAPI/DBus/DBusProxyConnection.h | 6 +- src/CommonAPI/DBus/DBusProxyHelper.h | 351 +- src/CommonAPI/DBus/DBusProxyManager.cpp | 151 + src/CommonAPI/DBus/DBusProxyManager.h | 71 + src/CommonAPI/DBus/DBusServicePublisher.cpp | 129 +- src/CommonAPI/DBus/DBusServicePublisher.h | 24 +- src/CommonAPI/DBus/DBusServiceRegistry.cpp | 1367 ++- src/CommonAPI/DBus/DBusServiceRegistry.h | 260 +- src/CommonAPI/DBus/DBusServiceStatusEvent.cpp | 53 - src/CommonAPI/DBus/DBusServiceStatusEvent.h | 47 - src/CommonAPI/DBus/DBusStubAdapter.cpp | 28 +- src/CommonAPI/DBus/DBusStubAdapter.h | 42 +- src/CommonAPI/DBus/DBusStubAdapterHelper.h | 8 +- src/pugixml/pugiconfig.hpp | 69 + src/pugixml/pugixml.cpp | 10250 +++++++++++++++++++ src/pugixml/pugixml.hpp | 1265 +++ src/test/DBusConnectionTest.cpp | 29 +- src/test/DBusManagedTest.cpp | 351 + src/test/DBusObjectManagerStubTest.cpp | 472 + src/test/DBusProxyTest.cpp | 88 +- src/test/DBusSelectiveBroadcastTest.cpp | 5 +- src/test/DBusServiceRegistryTest.cpp | 467 +- src/test/DBusTestUtils.h | 14 +- src/test/commonapi/tests/DerivedTypeCollection.cpp | 20 +- src/test/commonapi/tests/DerivedTypeCollection.h | 56 +- .../commonapi/tests/PredefinedTypeCollection.h | 23 +- src/test/commonapi/tests/TestInterface.h | 10 +- .../commonapi/tests/TestInterfaceDBusProxy.cpp | 31 +- src/test/commonapi/tests/TestInterfaceDBusProxy.h | 21 +- .../tests/TestInterfaceDBusStubAdapter.cpp | 86 +- .../commonapi/tests/TestInterfaceDBusStubAdapter.h | 28 +- src/test/commonapi/tests/TestInterfaceProxy.h | 80 +- src/test/commonapi/tests/TestInterfaceProxyBase.h | 10 +- src/test/commonapi/tests/TestInterfaceStub.h | 53 +- .../commonapi/tests/TestInterfaceStubDefault.cpp | 120 +- .../commonapi/tests/TestInterfaceStubDefault.h | 31 +- src/test/commonapi/tests/managed/BranchInterface.h | 90 + .../tests/managed/BranchInterfaceDBusProxy.cpp | 74 + .../tests/managed/BranchInterfaceDBusProxy.h | 61 + .../managed/BranchInterfaceDBusStubAdapter.cpp | 89 + .../tests/managed/BranchInterfaceDBusStubAdapter.h | 62 + .../commonapi/tests/managed/BranchInterfaceProxy.h | 174 + .../tests/managed/BranchInterfaceProxyBase.h | 47 + .../commonapi/tests/managed/BranchInterfaceStub.h | 91 + .../tests/managed/BranchInterfaceStubDefault.cpp | 43 + .../tests/managed/BranchInterfaceStubDefault.h | 63 + src/test/commonapi/tests/managed/LeafInterface.h | 90 + .../tests/managed/LeafInterfaceDBusProxy.cpp | 74 + .../tests/managed/LeafInterfaceDBusProxy.h | 61 + .../tests/managed/LeafInterfaceDBusStubAdapter.cpp | 89 + .../tests/managed/LeafInterfaceDBusStubAdapter.h | 62 + .../commonapi/tests/managed/LeafInterfaceProxy.h | 174 + .../tests/managed/LeafInterfaceProxyBase.h | 47 + .../commonapi/tests/managed/LeafInterfaceStub.h | 91 + .../tests/managed/LeafInterfaceStubDefault.cpp | 43 + .../tests/managed/LeafInterfaceStubDefault.h | 63 + src/test/commonapi/tests/managed/RootInterface.h | 91 + .../tests/managed/RootInterfaceDBusProxy.cpp | 82 + .../tests/managed/RootInterfaceDBusProxy.h | 66 + .../tests/managed/RootInterfaceDBusStubAdapter.cpp | 203 + .../tests/managed/RootInterfaceDBusStubAdapter.h | 70 + .../commonapi/tests/managed/RootInterfaceProxy.h | 184 + .../tests/managed/RootInterfaceProxyBase.h | 52 + .../commonapi/tests/managed/RootInterfaceStub.h | 105 + .../tests/managed/RootInterfaceStubDefault.cpp | 76 + .../tests/managed/RootInterfaceStubDefault.h | 72 + src/test/fake/legacy/service/LegacyInterface.h | 10 +- .../legacy/service/LegacyInterfaceDBusProxy.cpp | 22 +- .../fake/legacy/service/LegacyInterfaceDBusProxy.h | 18 +- .../service/LegacyInterfaceDBusStubAdapter.cpp | 105 + .../service/LegacyInterfaceDBusStubAdapter.h | 62 + .../fake/legacy/service/LegacyInterfaceProxy.h | 59 +- .../fake/legacy/service/LegacyInterfaceProxyBase.h | 106 +- src/test/fake/legacy/service/LegacyInterfaceStub.h | 95 + .../legacy/service/LegacyInterfaceStubDefault.cpp | 59 + .../legacy/service/LegacyInterfaceStubDefault.h | 69 + src/test/fakeLegacyService.fidl | 24 + src/test/fakeLegacyService/fakeLegacyService.fidl | 24 - src/test/leaf.fidl | 26 + src/test/root.fidl | 51 + 100 files changed, 19363 insertions(+), 1439 deletions(-) create mode 100644 src/CommonAPI/DBus/DBusInstanceAvailabilityStatusChangedEvent.h create mode 100644 src/CommonAPI/DBus/DBusInterfaceHandler.h create mode 100644 src/CommonAPI/DBus/DBusObjectManagerStub.cpp create mode 100644 src/CommonAPI/DBus/DBusObjectManagerStub.h create mode 100644 src/CommonAPI/DBus/DBusProxyManager.cpp create mode 100644 src/CommonAPI/DBus/DBusProxyManager.h delete mode 100644 src/CommonAPI/DBus/DBusServiceStatusEvent.cpp delete mode 100644 src/CommonAPI/DBus/DBusServiceStatusEvent.h create mode 100644 src/pugixml/pugiconfig.hpp create mode 100644 src/pugixml/pugixml.cpp create mode 100644 src/pugixml/pugixml.hpp create mode 100644 src/test/DBusManagedTest.cpp create mode 100644 src/test/DBusObjectManagerStubTest.cpp create mode 100644 src/test/commonapi/tests/managed/BranchInterface.h create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.cpp create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceDBusProxy.h create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.cpp create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceDBusStubAdapter.h create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceProxy.h create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceProxyBase.h create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceStub.h create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceStubDefault.cpp create mode 100644 src/test/commonapi/tests/managed/BranchInterfaceStubDefault.h create mode 100644 src/test/commonapi/tests/managed/LeafInterface.h create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.cpp create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceDBusProxy.h create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.cpp create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceDBusStubAdapter.h create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceProxy.h create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceProxyBase.h create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceStub.h create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceStubDefault.cpp create mode 100644 src/test/commonapi/tests/managed/LeafInterfaceStubDefault.h create mode 100644 src/test/commonapi/tests/managed/RootInterface.h create mode 100644 src/test/commonapi/tests/managed/RootInterfaceDBusProxy.cpp create mode 100644 src/test/commonapi/tests/managed/RootInterfaceDBusProxy.h create mode 100644 src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.cpp create mode 100644 src/test/commonapi/tests/managed/RootInterfaceDBusStubAdapter.h create mode 100644 src/test/commonapi/tests/managed/RootInterfaceProxy.h create mode 100644 src/test/commonapi/tests/managed/RootInterfaceProxyBase.h create mode 100644 src/test/commonapi/tests/managed/RootInterfaceStub.h create mode 100644 src/test/commonapi/tests/managed/RootInterfaceStubDefault.cpp create mode 100644 src/test/commonapi/tests/managed/RootInterfaceStubDefault.h create mode 100644 src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.cpp create mode 100644 src/test/fake/legacy/service/LegacyInterfaceDBusStubAdapter.h create mode 100644 src/test/fake/legacy/service/LegacyInterfaceStub.h create mode 100644 src/test/fake/legacy/service/LegacyInterfaceStubDefault.cpp create mode 100644 src/test/fake/legacy/service/LegacyInterfaceStubDefault.h create mode 100644 src/test/fakeLegacyService.fidl delete mode 100644 src/test/fakeLegacyService/fakeLegacyService.fidl create mode 100644 src/test/leaf.fidl create mode 100644 src/test/root.fidl 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: "_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 #include #include +#include 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 commonApiAddressDetails_; std::unordered_map 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(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(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 DBusConnection::getDBusServiceRegistr return serviceRegistry; } +//Does this need to be a weak pointer? const std::shared_ptr 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 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 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 +void notifyDBusSignalHandlers(DBusSignalHandlersTable& dbusSignalHandlerstable, + std::pair& 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 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 +#include + + 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 dbusObjectManagerSignalMatchRulesMap_; + std::unordered_multimap 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 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& 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 DBusDaemonProxy::getManagedObjectsAsync(const std::strin 2000); } +std::future DBusDaemonProxy::getNameOwnerAsync(const std::string& busName, GetNameOwnerAsyncCallback getNameOwnerAsyncCallback) const { + DBusMessage dbusMessage = createMethodCall("GetNameOwner", "s"); + + DBusOutputStream outputStream(dbusMessage); + const bool success = DBusSerializableArguments::serialize(outputStream, busName); + if (!success) { + std::promise promise; + promise.set_value(CallStatus::OUT_OF_MEMORY); + return promise.get_future(); + } + outputStream.flush(); + + return getDBusConnection()->sendDBusMessageWithReplyAsync( + dbusMessage, + DBusProxyAsyncCallbackHandler::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 NameOwnerChangedEvent; + typedef Event NameOwnerChangedEvent; - typedef std::unordered_map PropertyDictStub; - typedef std::unordered_map InterfaceToPropertyDict; - typedef std::unordered_map DBusObjectToInterfaceDict; + typedef std::unordered_map PropertyDictStub; + typedef std::unordered_map InterfaceToPropertyDict; + typedef std::unordered_map DBusObjectToInterfaceDict; - typedef std::function)> ListNamesAsyncCallback; - typedef std::function NameHasOwnerAsyncCallback; - typedef std::function GetManagedObjectsAsyncCallback; + typedef std::function)> ListNamesAsyncCallback; + typedef std::function NameHasOwnerAsyncCallback; + typedef std::function GetManagedObjectsAsyncCallback; + typedef std::function GetNameOwnerAsyncCallback; + DBusDaemonProxy(const std::shared_ptr& dbusConnection); - DBusDaemonProxy(const std::shared_ptr& 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 listNamesAsync(ListNamesAsyncCallback listNamesAsyncCallback) const; void nameHasOwner(const std::string& busName, CommonAPI::CallStatus& callStatus, bool& hasOwner) const; - std::future nameHasOwnerAsync(const std::string& busName, NameHasOwnerAsyncCallback nameHasOwnerAsyncCallback) const; - - std::future getManagedObjectsAsync(const std::string& forDBusServiceName, GetManagedObjectsAsyncCallback) const; + std::future nameHasOwnerAsync(const std::string& busName, + NameHasOwnerAsyncCallback nameHasOwnerAsyncCallback) const; + + std::future 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 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 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 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 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 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 proxy = (it->second)(shared_from_this(), commonApiAddress, interfaceName, connectionName, objectPath, dbusConnection_); + proxy->init(); + return proxy; } } return NULL; } -bool DBusFactory::registerAdapter(std::shared_ptr stubBase, - const char* interfaceId, - const std::string& participantId, - const std::string& serviceName, - const std::string& domain) { +std::shared_ptr DBusFactory::createDBusStubAdapter(const std::shared_ptr& 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, 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 {}; } - auto foundFunction = registeredAdapterFactoryFunctions_->find(interfaceId); - if(foundFunction != registeredAdapterFactoryFunctions_->end()) { - std::shared_ptr 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; + + 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 (*DBusProxyFactoryFunction) (const std::string& commonApiAddress, - const std::string& interfaceName, - const std::string& busName, - const std::string& objectPath, - const std::shared_ptr& dbusProxyConnection); +typedef std::shared_ptr (*DBusProxyFactoryFunction)(const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection); -typedef std::shared_ptr (*DBusAdapterFactoryFunction) (const std::string& commonApiAddress, +typedef std::shared_ptr (*DBusAdapterFactoryFunction) (const std::shared_ptr& factory, + const std::string& commonApiAddress, const std::string& interfaceName, const std::string& busName, const std::string& objectPath, const std::shared_ptr& dbusProxyConnection, const std::shared_ptr& stubBase); -class DBusFactory: public Factory { +class DBusFactory: public Factory, public std::enable_shared_from_this { public: DBusFactory(std::shared_ptr runtime, const MiddlewareInfo* middlewareInfo, std::shared_ptr 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 createDBusStubAdapter(const std::shared_ptr& stubBase, + const char* interfaceId, + const std::string& participantId, + const std::string& serviceName, + const std::string& domain); + + std::shared_ptr getDbusConnection(); + virtual std::shared_ptr createProxy(const char* interfaceId, const std::string& participantId, const std::string& serviceName, const std::string& domain); - virtual bool registerAdapter(std::shared_ptr 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 serviceRegistry); std::shared_ptr dbusConnection_; std::shared_ptr 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 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 + +#include "DBusProxy.h" +#include "DBusObjectManagerStub.h" +#include "DBusInstanceAvailabilityStatusChangedEvent.h" + +#include +#include +#include +#include + + +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 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 + +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& 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 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 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 lockedConnection = dbusConnection_.lock(); - if (lockedConnection) { - lockedConnection->unregisterObjectPath(objectPath); + if (!isDeregistrationSuccessful) { + const bool isDBusStubAdapterAdded = addDBusInterfaceHandler(dbusStubAdapterHandlerPath, dbusStubAdapter); + assert(isDBusStubAdapterAdded); + } + } + + if (isDeregistrationSuccessful) { + std::shared_ptr 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 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 elems = CommonAPI::split(dbusObjectPath, '/'); if (dbusMessage.hasObjectPath(dbusObjectPath)) { foundRegisteredObjects = true; xmlData << "\n" - << dbusStubAdapter->getMethodsDBusIntrospectionXmlData() << "\n" + << dbusStubAdapterBase->getMethodsDBusIntrospectionXmlData() << "\n" "\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 DBusInterfaceHandlerPath; -typedef DBusInterfaceHandlerPath DBusInterfaceHandlerToken; - class DBusStubAdapter; +class DBusInterfaceHandler; class DBusObjectManager { public: DBusObjectManager(const std::shared_ptr&); ~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 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 DBusRegisteredObjectsTable; + typedef std::unordered_map DBusRegisteredObjectsTable; DBusRegisteredObjectsTable dbusRegisteredObjectsTable_; + DBusObjectManagerStub rootDBusObjectManagerStub_; + std::weak_ptr 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 +#include + +namespace CommonAPI { +namespace DBus { + +DBusObjectManagerStub::DBusObjectManagerStub(const std::string& dbusObjectPath, + const std::shared_ptr& 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 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 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 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& 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& 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 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 "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ""; +} + +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 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 +#include +#include + +namespace CommonAPI { +namespace DBus { + +class DBusStubAdapter; + +/** + * Stub for standard org.freedesktop.dbus.ObjectManager 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 DBusPropertiesChangedDict; + typedef std::unordered_map DBusInterfacesAndPropertiesDict; + typedef std::unordered_map DBusObjectPathAndInterfacesDict; + + public: + DBusObjectManagerStub(const std::string& dbusObjectPath, const std::shared_ptr&); + + /** + * 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 + * InsterfaceAdded + * 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 + * InsterfaceRemoved + * 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& dbusConnection) const; + + bool emitInterfacesRemovedSignal(DBusStubAdapter* dbusStubAdapter, + const std::shared_ptr& dbusConnection) const; + + std::string dbusObjectPath_; + std::weak_ptr dbusConnectionWeakPtr_; + + typedef std::unordered_map DBusInterfacesMap; + typedef std::unordered_map 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 +#include namespace CommonAPI { @@ -28,12 +29,14 @@ void DBusProxyStatusEvent::onListenerAdded(const CancellableListener& listener) } -DBusProxy::DBusProxy(const std::string& commonApiAddress, +DBusProxy::DBusProxy(const std::shared_ptr& factory, + const std::string& commonApiAddress, const std::string& dbusInterfaceName, const std::string& dbusBusName, const std::string& dbusObjectPath, const std::shared_ptr& 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 #include @@ -39,7 +40,8 @@ class DBusProxyStatusEvent: public ProxyStatusEvent { class DBusProxy: public DBusProxyBase { public: - DBusProxy(const std::string& commonApiAddress, + DBusProxy(const std::shared_ptr& 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_; @@ -90,6 +93,8 @@ class DBusProxy: public DBusProxyBase { const std::string dbusBusName_; const std::string dbusObjectPath_; const std::string dbusInterfaceName_; + + const std::shared_ptr& 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 #include @@ -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 getDBusServiceRegistry() = 0; virtual const std::shared_ptr 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 _In, class... _InArgs, - template class _Out, class... _OutArgs> -struct DBusProxyHelper<_In<_InArgs...>, _Out<_OutArgs...>> { - template - 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 - 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 _In, class... _InArgs, + template class _Out, class... _OutArgs> + struct DBusProxyHelper<_In<_InArgs...>, _Out<_OutArgs...>> { + + template + 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 + 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 static void callMethodWithReply( @@ -123,114 +125,155 @@ struct DBusProxyHelper<_In<_InArgs...>, _Out<_OutArgs...>> { } } - template - static void callMethodWithReply( - const _DBusProxy& dbusProxy, - const char* methodName, - const char* methodSignature, - const _InArgs&... inArgs, - CommonAPI::CallStatus& callStatus, - _OutArgs&... outArgs) { + template + 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 + 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 - static std::future 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 + static std::future 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::type()); - CallStatus callStatus = CallStatus::NOT_AVAILABLE; + std::promise promise; + promise.set_value(callStatus); + return promise.get_future(); + } - callCallbackOnNotAvailable(asyncCallback, typename make_sequence::type()); + } - std::promise promise; - promise.set_value(callStatus); - return promise.get_future(); - } + template + static std::future 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 static std::future 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::type()); + + std::promise promise; + promise.set_value(callStatus); + return promise.get_future(); + } + } - callCallbackOnNotAvailable(asyncCallback, typename make_sequence::type()); - std::promise promise; - promise.set_value(callStatus); - return promise.get_future(); + template + static std::future 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 promise; + promise.set_value(CallStatus::OUT_OF_MEMORY); + return promise.get_future(); } - } - - - template - static std::future 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 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 - static void callCallbackOnNotAvailable(std::function 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 + static void callCallbackOnNotAvailable(std::function 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& 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 returnVec; + if (status == CommonAPI::CallStatus::SUCCESS) { + translateCommonApiAddresses(dict, returnVec); + } + call(status, returnVec); +} + +void DBusProxyManager::getAvailableInstances(CommonAPI::CallStatus& callStatus, std::vector& availableInstances) { + DBusObjectManagerStub::DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict; + + DBusProxyHelper, + DBusSerializableArguments >::callMethodWithReply( + dbusProxy_, + DBusObjectManagerStub::getInterfaceName(), + "GetManagedObjects", + "a{oa{sa{sv}}}", + callStatus, + dbusObjectPathAndInterfacesDict); + + + + if (callStatus == CallStatus::SUCCESS) { + translateCommonApiAddresses(dbusObjectPathAndInterfacesDict, availableInstances); + } +} + +std::future DBusProxyManager::getAvailableInstancesAsync(GetAvailableInstancesCallback callback) { + return CommonAPI::DBus::DBusProxyHelper, + 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 >& callStatus) { + call(CallStatus::SUCCESS, alive); + callStatus->set_value(CallStatus::SUCCESS); + return SubscriptionStatus::CANCEL; +} + +std::future DBusProxyManager::getInstanceAvailabilityStatusAsync(const std::string& instanceAddress, + GetInstanceAvailabilityStatusCallback callback) { + std::stringstream ss; + ss << "local:" << interfaceName_ << ":" << instanceAddress; + + + std::shared_ptr > promise = std::make_shared>(); + 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& 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 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 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 + +#include "DBusProxy.h" +#include "DBusFactory.h" +#include "DBusObjectManagerStub.h" +#include "DBusInstanceAvailabilityStatusChangedEvent.h" + +#include +#include +#include +#include + + +namespace CommonAPI { +namespace DBus { + +class DBusProxyManager: public ProxyManager { + public: + DBusProxyManager(DBusProxy& dbusProxy, const char* interfaceName, const std::shared_ptr& factory); + + virtual void getAvailableInstances(CommonAPI::CallStatus&, std::vector& availableInstances); + virtual std::future getAvailableInstancesAsync(GetAvailableInstancesCallback callback); + + virtual void getInstanceAvailabilityStatus(const std::string& instanceAddress, + CallStatus& callStatus, + AvailabilityStatus& availabilityStatus); + + virtual std::future getInstanceAvailabilityStatusAsync(const std::string&, + GetInstanceAvailabilityStatusCallback callback); + + virtual InstanceAvailabilityStatusChangedEvent& getInstanceAvailabilityStatusChangedEvent(); + protected: + virtual std::shared_ptr 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 >& callStatus); + + void translateCommonApiAddresses(const DBusObjectManagerStub::DBusObjectPathAndInterfacesDict& dbusObjectPathAndInterfacesDict, + std::vector& commonApiAddresses); + + DBusProxy& dbusProxy_; + DBusInstanceAvailabilityStatusChangedEvent dbusInstanceAvailabilityStatusEvent_; + const std::shared_ptr factory_; + const std::shared_ptr 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 namespace CommonAPI { @@ -20,20 +23,130 @@ std::shared_ptr DBusServicePublisher::getInstance() { return instance; } -bool DBusServicePublisher::registerService(const std::string& serviceAddress, std::shared_ptr adapter) { - return registeredServices_.insert( {serviceAddress, adapter} ).second; +bool DBusServicePublisher::registerService(const std::shared_ptr& 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 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, + const char* interfaceId, + const std::string& participantId, + const std::string& serviceName, + const std::string& domain, + const std::shared_ptr& factory) { + auto dbusFactory = std::dynamic_pointer_cast(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 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& 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 getInstance(); - bool registerService(const std::string& serviceAddress, std::shared_ptr adapter); + bool registerService(const std::shared_ptr& dbusStubAdapter); + virtual bool unregisterService(const std::string& serviceAddress); - bool unregisterService(const std::string& serviceAddress); + bool registerManagedService(const std::shared_ptr& managedDBusStubAdapter); + bool unregisterManagedService(const std::string& serviceAddress); + + std::shared_ptr getRegisteredService(const std::string&); + + protected: + virtual bool registerService(const std::shared_ptr& stubBase, + const char* interfaceId, + const std::string& participantId, + const std::string& serviceName, + const std::string& domain, + const std::shared_ptr& factory); private: - std::unordered_map> registeredServices_; + typedef std::unordered_map> 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 +#include namespace CommonAPI { namespace DBus { -DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr dbusProxyConnection): +DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr dbusProxyConnection) : dbusDaemonProxy_(std::make_shared(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 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& 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(clock::now() - startTimePoint); + std::lock_guard 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 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 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::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 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::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 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 availableServiceNames; + + dbusDaemonProxy_->listNames(callStatus, availableServiceNames); + + if(callStatus == CallStatus::SUCCESS) { + for(std::string serviceName : availableServiceNames) { + if(isDBusServiceName(serviceName)) { + dbusServiceNameMap_[serviceName]; + } + } + } +} + +// d-feet mode std::vector DBusServiceRegistry::getAvailableServiceInstances(const std::string& interfaceName, const std::string& domainName) { std::vector 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 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 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 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 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(); +// } else { +// rec->dbusObjectPathsCache +// } +// +// } else { +// return std::vector(); +// } +//} void DBusServiceRegistry::getAvailableServiceInstancesAsync(Factory::GetAvailableServiceInstancesCallback callback, const std::string& interfaceName, const std::string& domainName) { - std::vector 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 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 dbusInterfaceNames; + DBusRecordState dbusInterfaceNameState; + + dbusInputStream >> dbusObjectPath; + + bool added = false; + + if (dbusMessage.hasMemberName("InterfacesAdded")) { + added = true; + dbusInterfaceNameState = DBusRecordState::AVAILABLE; + + typedef std::unordered_map DBusPropertiesChangedDict; + typedef std::unordered_map DBusInterfacesAndPropertiesDict; + typedef std::unordered_map DBusObjectPathAndInterfacesDict; + DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict; + dbusInputStream >> dbusObjectPathAndInterfacesDict; + + for (auto& dbusInterfaceIterator : dbusObjectPathAndInterfacesDict) { + const auto& dbusInterfaceName = dbusInterfaceIterator.first; + dbusInterfaceNames.insert(dbusInterfaceName); + } + } else { + std::vector 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 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 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& 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 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 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 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::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::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 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 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 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 dbusServicesLock(dbusServicesMutex_); - notificationThread_ = std::this_thread::get_id(); - onDBusServiceAvailabilityStatus(affectedName, dbusServiceAvailabilityStatus); - notificationThread_ = std::thread::id(); + std::lock_guard 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 dbusNames) { - std::lock_guard 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 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 promiseOnResolve = std::move(dbusObjectPathsCache.promiseOnResolve); - if (availabilityStatus == AvailabilityStatus::AVAILABLE) { - const std::string& dbusServiceName = dbusServiceIterator->first; + try { + std::future 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& 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 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& 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 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& 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 +#include #include #include #include @@ -34,6 +37,7 @@ #include #include +#include namespace CommonAPI { namespace DBus { @@ -47,10 +51,10 @@ typedef std::pair DBusInstanceId; class DBusProxyConnection; class DBusDaemonProxy; - -class DBusServiceRegistry: public std::enable_shared_from_this { +class DBusServiceRegistry: public std::enable_shared_from_this, + 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 { typedef functor; typedef list; typedef subscription } typedef std::function DBusServiceListener; typedef std::list DBusServiceListenerList; - typedef DBusServiceListenerList::iterator Subscription; + typedef DBusServiceListenerList::iterator DBusServiceSubscription; + - typedef std::pair DBusObjectInterfacePair; - typedef std::unordered_map > DBusInstanceList; - typedef std::unordered_map > DBusServiceList; + typedef std::function& interfaces, + const AvailabilityStatus& availabilityStatus)> DBusManagedInterfaceListener; + typedef std::list DBusManagedInterfaceListenerList; + typedef DBusManagedInterfaceListenerList::iterator DBusManagedInterfaceSubscription; DBusServiceRegistry(std::shared_ptr 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 getAvailableServiceInstances(const std::string& interfaceName, const std::string& domainName = "local"); @@ -87,57 +101,223 @@ class DBusServiceRegistry: public std::enable_shared_from_this getManagedObjects(const std::string& connectionName, const std::string& objectpath); - void onListNamesCallback(const CommonAPI::CallStatus& callStatus, std::vector 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 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 promiseOnResolve; + std::shared_future futureOnResolve; + std::unique_lock* mutexOnResolve; + + std::unordered_map dbusObjectPathListenersMap; + + /* per manager records: anser commonapi queries and update after dbus GetManagedObjects */ +// typedef std::unordered_set DBusObjectPathsSet; +// typedef std::pair DBusManagedInterfaceRecord; +// typedef std::unordered_map DBusManagedInterfacesMap; +// typedef std::pair DBusObjectManagerRecord; +// std::unordered_map dbusObjectManagersMap; + }; - size_t getResolvedServiceInstances(const std::string& dbusInterfaceName, std::vector& availableServiceInstances); - size_t getNumResolvingServiceInstances(); + std::unordered_map dbusServiceListenersMap; - bool waitDBusServicesAvailable(std::unique_lock& 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 promiseOnResolve; - static bool isDBusServiceName(const std::string& name); + std::unordered_set 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 ownedBusNames; + std::unordered_map dbusObjectPathsCache; + + // TODO managed objects + }; - std::shared_ptr dbusDaemonProxy_; + std::unordered_map dbusUniqueNamesMap_; + // mapping service names (well-known names) to service instances + std::unordered_map 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_; 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& dbusInterfaceNames, + const DBusRecordState& dbusInterfaceNamesState); + + void notifyDBusObjectPathResolved(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap, + const std::unordered_set& dbusInterfaceNames); + + void notifyDBusObjectPathChanged(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap, + const std::unordered_set& 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 - -namespace CommonAPI { -namespace DBus { - -DBusServiceStatusEvent::DBusServiceStatusEvent(std::shared_ptr 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 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 -#include - -#include -#include - -namespace CommonAPI { -namespace DBus { - - -class DBusServiceRegistry; - -class DBusServiceStatusEvent: public DBusMultiEvent { - public: - DBusServiceStatusEvent(std::shared_ptr 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 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& factory, + const std::string& commonApiAddress, const std::string& dbusInterfaceName, const std::string& dbusBusName, const std::string& dbusObjectPath, - const std::shared_ptr& dbusConnection) : + const std::shared_ptr& 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 @@ -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& factory, + const std::string& commonApiAddress, const std::string& dbusInterfaceName, const std::string& dbusBusName, const std::string& dbusObjectPath, - const std::shared_ptr& dbusConnection); + const std::shared_ptr& 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& 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 dbusConnection_; - bool isInitialized_; - - DBusInterfaceHandlerToken dbusInterfaceHandlerToken_; + DBusObjectManagerStub* managerStub; static const std::string domain_; + + const std::shared_ptr 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& factory, + const std::string& commonApiAddress, const std::string& dbusInterfaceName, const std::string& dbusBusName, const std::string& dbusObjectPath, const std::shared_ptr& 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 +#include +#include +#include +#include + +#ifndef PUGIXML_NO_XPATH +# include +# include +# ifdef PUGIXML_NO_EXCEPTIONS +# include +# endif +#endif + +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// For placement new +#include + +#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 +#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 + struct xml_memory_management_function_storage + { + static allocation_function allocate; + static deallocation_function deallocate; + }; + + template allocation_function xml_memory_management_function_storage::allocate = default_allocate; + template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; + + typedef xml_memory_management_function_storage 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(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((reinterpret_cast(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(allocate_memory(full_size, page)); + + if (!header) return 0; + + // setup header + ptrdiff_t page_offset = reinterpret_cast(header) - page->data; + + assert(page_offset >= 0 && page_offset < (1 << 16)); + header->page_offset = static_cast(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(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(static_cast(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(static_cast(string)) - 1; + + // deallocate + size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset; + xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(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(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(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(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(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(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(((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(ch); + return result + 1; + } + // U+0080..U+07FF + else if (ch < 0x800) + { + result[0] = static_cast(0xC0 | (ch >> 6)); + result[1] = static_cast(0x80 | (ch & 0x3F)); + return result + 2; + } + // U+0800..U+FFFF + else + { + result[0] = static_cast(0xE0 | (ch >> 12)); + result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[2] = static_cast(0x80 | (ch & 0x3F)); + return result + 3; + } + } + + static value_type high(value_type result, uint32_t ch) + { + // U+10000..U+10FFFF + result[0] = static_cast(0xF0 | (ch >> 18)); + result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); + result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[3] = static_cast(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(ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + uint32_t msh = static_cast(ch - 0x10000) >> 10; + uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; + + result[0] = static_cast(0xD800 + msh); + result[1] = static_cast(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(ch > 255 ? '?' : ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + (void)ch; + + *result = '?'; + + return result + 1; + } + }; + + template 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::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + template 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(data) & 3) == 0) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + while (size >= 4 && (*static_cast(static_cast(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(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(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(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(lead - 0xE000) < 0x2000) + { + result = Traits::low(result, lead); + data += 1; + } + // surrogate pair lead + else if (static_cast(lead - 0xD800) < 0x400 && data + 1 < end) + { + uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; + + if (static_cast(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::type*>(data), size, result); + } + }; + + template 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(endian_swap(static_cast::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(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) +#else + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(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(&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 <, (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(const_cast(contents)); + } + else + { + void* buffer = xml_memory::allocate(size > 0 ? size : 1); + if (!buffer) return false; + + memcpy(buffer, contents, size); + + out_buffer = static_cast(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(contents); + + if (is_mutable) + { + out_buffer = const_cast(data); + } + else + { + out_buffer = static_cast(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(contents); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf8_block(data, size, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf8_block(data, size, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template 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(contents); + size_t length = size / sizeof(uint16_t); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf16_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf16_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template 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(contents); + size_t length = size / sizeof(uint32_t); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf32_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + wchar_writer::value_type out_end = utf_decoder::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(contents); + + // get length in wchar_t units + out_length = size; + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + wchar_writer::value_type out_end = utf_decoder::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 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(contents); + size_t length = size / sizeof(uint16_t); + + // first pass: get length in utf8 units + out_length = utf_decoder::decode_utf16_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + uint8_t* out_end = utf_decoder::decode_utf16_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template 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(contents); + size_t length = size / sizeof(uint32_t); + + // first pass: get length in utf8 units + out_length = utf_decoder::decode_utf32_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + uint8_t* out_end = utf_decoder::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(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::decode_latin1_block(postfix, postfix_length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(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(out_buffer); + uint8_t* out_end = utf_decoder::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::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(buffer); + uint8_t* end = utf_decoder::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 as_wide_impl(const char* str, size_t size) + { + const uint8_t* data = reinterpret_cast(str); + + // first pass: get length in wchar_t units + size_t length = utf_decoder::decode_utf8_block(data, size, 0); + + // allocate resulting string + std::basic_string result; + result.resize(length); + + // second pass: convert to wchar_t + if (length > 0) + { + wchar_writer::value_type begin = reinterpret_cast(&result[0]); + wchar_writer::value_type end = utf_decoder::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(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(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(s) - reinterpret_cast(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(s) - reinterpret_cast(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(ch - '0') <= 9) + ucsc = 16 * ucsc + (ch - '0'); + else if (static_cast((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(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(wchar_writer::any(reinterpret_cast(s), ucsc)); + #else + s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); + #endif + + g.push(s, stre - s); + return stre; + } + + case 'a': // &a + { + ++stre; + + if (*stre == 'm') // &am + { + if (*++stre == 'p' && *++stre == ';') // & + { + *s++ = '&'; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + else if (*stre == 'p') // &ap + { + if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' + { + *s++ = '\''; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + break; + } + + case 'g': // &g + { + if (*++stre == 't' && *++stre == ';') // > + { + *s++ = '>'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'l': // &l + { + if (*++stre == 't' && *++stre == ';') // < + { + *s++ = '<'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'q': // &q + { + if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " + { + *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 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::parse; + case 1: return strconv_pcdata_impl::parse; + case 2: return strconv_pcdata_impl::parse; + case 3: return strconv_pcdata_impl::parse; + default: return 0; // should not get here + } + } + + typedef char_t* (*strconv_attribute_t)(char_t*, char_t); + + template 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::parse_simple; + case 1: return strconv_attribute_impl::parse_simple; + case 2: return strconv_attribute_impl::parse_eol; + case 3: return strconv_attribute_impl::parse_eol; + case 4: return strconv_attribute_impl::parse_wconv; + case 5: return strconv_attribute_impl::parse_wconv; + case 6: return strconv_attribute_impl::parse_wconv; + case 7: return strconv_attribute_impl::parse_wconv; + case 8: return strconv_attribute_impl::parse_wnorm; + case 9: return strconv_attribute_impl::parse_wnorm; + case 10: return strconv_attribute_impl::parse_wnorm; + case 11: return strconv_attribute_impl::parse_wnorm; + case 12: return strconv_attribute_impl::parse_wnorm; + case 13: return strconv_attribute_impl::parse_wnorm; + case 14: return strconv_attribute_impl::parse_wnorm; + case 15: return strconv_attribute_impl::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(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 == '-') // '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 == '[') + { + // '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 == '?') // 'header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES; + } + else if (*s == '!') // '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(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(result.offset) <= length); + + // update allocator state + *static_cast(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(static_cast(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::decode_wchar_block(data, length, dest); + + return static_cast(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::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(end - dest)); + + return static_cast(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::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(end - dest)); + + return static_cast(end - dest) * sizeof(uint32_t); + } + + // convert to latin1 + if (encoding == encoding_latin1) + { + uint8_t* dest = r_u8; + uint8_t* end = utf_decoder::decode_wchar_block(data, length, dest); + + return static_cast(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(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::decode_utf8_block(reinterpret_cast(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(end - dest)); + + return static_cast(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::decode_utf8_block(reinterpret_cast(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(end - dest)); + + return static_cast(end - dest) * sizeof(uint32_t); + } + + if (encoding == encoding_latin1) + { + uint8_t* dest = r_u8; + uint8_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); + + return static_cast(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(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(*s++); + assert(ch < 32); + + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((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(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((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(wcstol(value, 0, 10)); + #else + return static_cast(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(wcstoul(value, 0, 10)); + #else + return static_cast(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(wcstod(value, 0)); + #else + return static_cast(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(length); + + if (static_cast(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(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 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(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 PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + buffer_holder chunks(0, xml_stream_chunk::destroy); + + // read file to a chunk list + size_t total = 0; + xml_stream_chunk* last = 0; + + while (!stream.eof()) + { + // allocate new chunk + xml_stream_chunk* chunk = xml_stream_chunk::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(sizeof(chunk->data) / sizeof(T))); + chunk->size = static_cast(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(xml_memory::allocate(total)); + if (!buffer) return status_out_of_memory; + + char* write = buffer; + + for (xml_stream_chunk* chunk = static_cast*>(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 PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + // get length of remaining data in stream + typename std::basic_istream::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(length); + + if (static_cast(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(buffer.data), static_cast(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(stream.gcount()); + assert(actual_length <= read_length); + + *out_buffer = buffer.release(); + *out_size = actual_length * sizeof(T); + + return status_ok; + } + + template PUGI__FN xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& 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(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(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)); + (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 >& stream): narrow_stream(&stream), wide_stream(0) + { + } + + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& 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(data), static_cast(size)); + } + else + { + assert(wide_stream); + assert(size % sizeof(wchar_t) == 0); + + wide_stream->write(reinterpret_cast(data), static_cast(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(reinterpret_cast(_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::children() const + { + return xml_object_range(begin(), end()); + } + + PUGI__FN xml_object_range xml_node::children(const char_t* name_) const + { + return xml_object_range(xml_named_node_iterator(child(name_), name_), xml_named_node_iterator()); + } + + PUGI__FN xml_object_range xml_node::attributes() const + { + return xml_object_range(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((_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(_root->header & impl::xml_memory_page_pointer_mask); + + return xml_node(static_cast(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(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(reinterpret_cast(_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 >& 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 >& 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(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(&_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(&_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(&_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((reinterpret_cast(_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(_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(_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 >& 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 >& 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(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(bom)); + #else + buffered_writer.write('\xef', '\xbb', '\xbf'); + #endif + } + + if (!(flags & format_no_declaration) && !impl::has_declaration(*this)) + { + buffered_writer.write(PUGIXML_TEXT("'); + 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 >& 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 >& 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& str) + { + return impl::as_utf8_impl(str.c_str(), str.size()); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) + { + assert(str); + + return impl::as_wide_impl(str, strlen(str)); + } + + PUGI__FN std::basic_string 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 bool operator()(const T& lhs, const T& rhs) const + { + return lhs == rhs; + } + }; + + struct not_equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs != rhs; + } + }; + + struct less + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } + }; + + struct less_equal + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs <= rhs; + } + }; + + template void swap(T& lhs, T& rhs) + { + T temp = lhs; + lhs = rhs; + rhs = temp; + } + + template 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 void reverse(I begin, I end) + { + while (begin + 1 < end) swap(*begin++, *--end); + } + + template 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 void copy_backwards(I begin, I end, I target) + { + while (begin != end) *--target = *--end; + } + + template 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 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 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 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 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(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(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(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(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(alloc->reallocate(_uses_heap ? const_cast(_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(_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(ch - 'A') < 26 ? static_cast(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(*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(*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(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(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(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(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(*str++); + result += result << 10; + result ^= result >> 6; + } + + result += result << 3; + result ^= result >> 11; + result += result << 15; + + return result; + } + + template 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(name); + + case xpath_type_number: + return new_xpath_variable(name); + + case xpath_type_string: + return new_xpath_variable(name); + + case xpath_type_boolean: + return new_xpath_variable(name); + + default: + return 0; + } + } + + template 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(var)); + break; + + case xpath_type_number: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_string: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_boolean: + delete_xpath_variable(static_cast(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(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(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(_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(_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(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(_end - _begin); + size_t capacity = static_cast(_eos - _begin); + size_t count = static_cast(end_ - begin_); + + if (size_ + count > capacity) + { + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(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(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 struct axis_to_type + { + static const axis_t axis; + }; + + template const axis_t axis_to_type::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 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 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 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 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 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(type)), _rettype(static_cast(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(type)), _rettype(static_cast(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(type)), _rettype(static_cast(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(type)), _rettype(static_cast(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(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(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(c.size); + + case ast_func_position: + return static_cast(c.position); + + case ast_func_count: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_node_set(c, stack).size()); + } + + case ast_func_string_length_0: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(string_value(c.n, stack.result).length()); + } + + case ast_func_string_length_1: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_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(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(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(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(first); + size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(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()); + + case axis_ancestor_or_self: + return step_do(c, stack, axis_to_type()); + + case axis_attribute: + return step_do(c, stack, axis_to_type()); + + case axis_child: + return step_do(c, stack, axis_to_type()); + + case axis_descendant: + return step_do(c, stack, axis_to_type()); + + case axis_descendant_or_self: + return step_do(c, stack, axis_to_type()); + + case axis_following: + return step_do(c, stack, axis_to_type()); + + case axis_following_sibling: + return step_do(c, stack, axis_to_type()); + + case axis_namespace: + // namespaced axis is not supported + return xpath_node_set_raw(); + + case axis_parent: + return step_do(c, stack, axis_to_type()); + + case axis_preceding: + return step_do(c, stack, axis_to_type()); + + case axis_preceding_sibling: + return step_do(c, stack, axis_to_type()); + + case axis_self: + return step_do(c, stack, axis_to_type()); + + 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(_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(value.end - value.begin); + + char_t* c = static_cast(_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(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(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(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(this)->name; + + case xpath_type_number: + return static_cast(this)->name; + + case xpath_type_string: + return static_cast(this)->name; + + case xpath_type_boolean: + return static_cast(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(this)->value : false; + } + + PUGI__FN double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast(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(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(this)->value : impl::dummy_node_set; + } + + PUGI__FN bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast(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(this); + + // duplicate string + size_t size = (impl::strlength(value) + 1) * sizeof(char_t); + + char_t* copy = static_cast(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(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_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)->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)->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)->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), 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), 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)->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 + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#endif + +// Include STL headers +#ifndef PUGIXML_NO_STL +# include +# include +# include +#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, std::allocator > 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_pcdata, // Plain character data, i.e. 'text' + node_cdata, // Character data, i.e. '' + node_comment, // Comment tag, i.e. '' + node_pi, // Processing instruction, i.e. '' + node_declaration, // Document declaration, i.e. '' + node_doctype // Document type declaration, i.e. '' + }; + + // 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 < / 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 >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size); + + private: + std::basic_ostream >* narrow_stream; + std::basic_ostream >* 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 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 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 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 >& 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 >& 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 children() const; + xml_object_range children(const char_t* name) const; + xml_object_range 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 >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load(std::basic_istream >& 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 >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + void save(std::basic_ostream >& 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, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); + + // Convert UTF8 to wide string + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& 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>::create( - [&] (const CommonAPI::CallStatus&, std::vector) { - 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 + +#define COMMONAPI_INTERNAL_COMPILATION +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include +#include + +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::Runtime::load()); + serviceFactory = std::dynamic_pointer_cast(runtime->createFactory()); + clientFactory = std::dynamic_pointer_cast(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 runtime; + std::shared_ptr serviceFactory; + std::shared_ptr 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 proxyDBusConnection_; + std::shared_ptr 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(); + 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(); + 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(); + runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory); + + std::shared_ptr 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(); + + 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(); + runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory); + + std::shared_ptr 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(); + bool reg = rootStub->registerManagedStubLeafInterface(leafStub, leafInstance); + ASSERT_TRUE(reg); + + sleep(2); + + auto leafProxy = proxyManagerLeafInterface.buildProxy(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(); + runtime->getServicePublisher()->registerService(rootStub, rootAddress, serviceFactory); + + + std::shared_ptr 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(); + bool reg = rootStub->registerManagedStubLeafInterface(leafStub, leafInstance); + ASSERT_TRUE(reg); + + sleep(2); + + auto leafProxy = proxyManagerLeafInterface.buildProxy(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 + +#define COMMONAPI_INTERNAL_COMPILATION +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +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 factory, + const std::string& dbusObjectPath, + const std::shared_ptr& 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 factory, + const std::string& dbusObjectPath, + const std::string& dbusInterfaceName, + const std::shared_ptr& dbusConnection) : + DBusStubAdapter(factory, "local:" + dbusServiceName + ":" + dbusInterfaceName + "-" + dbusObjectPath, + dbusInterfaceName, + dbusServiceName, + dbusObjectPath, + dbusConnection) { + } + + +}; + + +class ManagerTestDBusStubAdapter: public TestDBusStubAdapter { + public: + ManagerTestDBusStubAdapter(const std::shared_ptr factory, const std::string& dbusObjectPath, + const std::shared_ptr& 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 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 create( + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection) { + auto dbusSignalHandler = new TestDBusObjectManagerSignalHandler(dbusObjectPath, dbusConnection); + dbusSignalHandler->init(); + return std::shared_ptr(dbusSignalHandler); + } + + private: + TestDBusObjectManagerSignalHandler(const std::string& dbusObjectPath, + const std::shared_ptr& 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 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::Runtime::load()); + serviceFactory = std::dynamic_pointer_cast(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 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& dbusSignalHandler, + const size_t& interfacesAddedCount, + const size_t& interfacesRemovedExpectedCount) { + const size_t waitMillisecondsPerInterface = 300; + + std::unique_lock 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& dbusSignalHandler, + const size_t& interfacesRemovedCount, + const size_t& interfacesAddedExpectedCount) { + const size_t waitMillisecondsPerInterface = 300; + + std::unique_lock 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 + void createDBusStubAdapter(std::array, _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 proxyDBusConnection_; + std::shared_ptr 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, 10> dbusStubAdapterArray; + + createDBusStubAdapter(dbusStubAdapterArray); + + const bool isServiceRegistrationSuccessful = std::all_of( + dbusStubAdapterArray.begin(), + dbusStubAdapterArray.end(), + [](const std::shared_ptr& 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& 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(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(serviceFactory, dbusObjectManagerStubPath, stubDBusConnection_); + managerDBusStubAdapter->init(); + + ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(managerDBusStubAdapter)); + + std::array, 10> dbusStubAdapterArray; + createDBusStubAdapter(dbusStubAdapterArray); + + const bool isServiceRegistrationSuccessful = std::all_of( + dbusStubAdapterArray.begin(), + dbusStubAdapterArray.end(), + [](const std::shared_ptr& 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& 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& dbusStubAdapter) { + return managerDBusStubAdapter->getDBusObjectManagerStub()->unexportDBusStubAdapter(dbusStubAdapter.get()); + }); + ASSERT_TRUE(isServiceUnexportSuccessful); + + const bool isServiceDeregistrationSuccessful = std::all_of( + dbusStubAdapterArray.begin(), + dbusStubAdapterArray.end(), + [](const std::shared_ptr& 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(serviceFactory, dbusObjectManagerStubPath, stubDBusConnection_); + managerDBusStubAdapter->init(); + + ASSERT_TRUE(CommonAPI::DBus::DBusServicePublisher::getInstance()->registerService(managerDBusStubAdapter)); + + std::array, 10> dbusStubAdapterArray; + createDBusStubAdapter(dbusStubAdapterArray); + + const bool isServiceRegistrationSuccessful = std::all_of( + dbusStubAdapterArray.begin(), + dbusStubAdapterArray.end(), + [](const std::shared_ptr& 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& 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& 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 #include #include +#include #include +#include +#include #include #include @@ -49,17 +52,26 @@ class ProxyTest: public ::testing::Test { protected: void SetUp() { + + auto runtime = std::dynamic_pointer_cast(CommonAPI::Runtime::load()); + + serviceFactory = std::dynamic_pointer_cast(runtime->createFactory()); + proxyDBusConnection_ = CommonAPI::DBus::DBusConnection::getSessionBus(); ASSERT_TRUE(proxyDBusConnection_->connect()); proxy_ = std::make_shared( + serviceFactory, commonApiAddress, interfaceName, busName, objectPath, proxyDBusConnection_); + proxy_->init(); } + std::shared_ptr serviceFactory; + virtual void TearDown() { usleep(30000); } @@ -70,6 +82,7 @@ protected: auto stubDefault = std::make_shared(); stubAdapter_ = std::make_shared( + 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 connection = proxy_->getDBusConnection(); - auto registry = connection->getDBusServiceRegistry(); - ASSERT_FALSE(!registry); + std::shared_ptr 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 wasCalledPromise; std::future 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 receivers = std::make_shared(); + 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 +#include #include #include +#include #include #include @@ -28,6 +30,9 @@ #include "DemoMainLoop.h" +#include +#include + // 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& 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 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 dbusServiceRegistry_; + CommonAPI::DBus::DBusServiceRegistry::DBusServiceSubscription dbusServiceSubscription_; +}; + + +class TestDBusStubAdapter: public CommonAPI::DBus::DBusStubAdapter { + public: + TestDBusStubAdapter(const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& 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::Runtime::load()); + + clientFactory = std::dynamic_pointer_cast(runtime->createFactory()); + serviceFactory = std::dynamic_pointer_cast(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 lock(testDBusServiceListener.lock); + auto waitResult = + testDBusServiceListener.statusChanged.wait_for( + lock, + std::chrono::milliseconds(4000), + [&]() {return testDBusServiceListener.lastAvailabilityStatus == availabilityStatus;}); + return waitResult; + } + + std::shared_ptr clientFactory; + std::shared_ptr serviceFactory; + + std::shared_ptr clientDBusConnection; + std::shared_ptr serviceDBusConnection; + + std::shared_ptr clientConnectionRegistry; + //std::shared_ptr serviceConnectionRegistry; }; -TEST_F(DBusServiceRegistryTest, CanBeConstructed) { - std::shared_ptr 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( + 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( + 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( + 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 > instanceStatusPromises; - std::unordered_map 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 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 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 > instanceStatusPromises; +// std::unordered_map 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 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 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 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 result = futureResult.get(); - ASSERT_EQ(1, result.size()); + EXPECT_EQ(1, result.size()); } TEST_F(DBusServiceDiscoveryTestWithPredefinedRemote, FindsNoInstancesOfNonexistingTestService) { std::vector 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(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(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(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(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 TestArrayTestStruct; - typedef std::unordered_map 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 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 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 createTestInterfaceDBusProxy( + const std::shared_ptr& factory, const std::string& commonApiAddress, const std::string& interfaceName, const std::string& busName, const std::string& objectPath, const std::shared_ptr& dbusProxyConnection) { - return std::make_shared(commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection); + return std::make_shared(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& factory, const std::string& commonApiAddress, const std::string& interfaceName, const std::string& busName, const std::string& objectPath, const std::shared_ptr& 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 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& 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 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 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> testPredefinedTypeAttribute_; - CommonAPI::DBus::DBusObservableAttribute> testDerivedStructAttribute_; - CommonAPI::DBus::DBusObservableAttribute> testDerivedArrayAttribute_; + CommonAPI::DBus::DBusObservableAttribute> testPredefinedTypeAttribute_; + CommonAPI::DBus::DBusObservableAttribute> testDerivedStructAttribute_; + CommonAPI::DBus::DBusObservableAttribute> testDerivedArrayAttribute_; CommonAPI::DBus::DBusEvent testPredefinedTypeBroadcast_; CommonAPI::DBus::DBusSelectiveEvent testSelectiveBroadcastSelective_; CommonAPI::DBus::DBusSelectiveEvent 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 @@ -12,13 +14,14 @@ namespace commonapi { namespace tests { std::shared_ptr createTestInterfaceDBusStubAdapter( + const std::shared_ptr& factory, const std::string& commonApiAddress, const std::string& interfaceName, const std::string& busName, const std::string& objectPath, const std::shared_ptr& dbusProxyConnection, const std::shared_ptr& stubBase) { - return std::make_shared(commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection, stubBase); + return std::make_shared(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& factory, const std::string& commonApiAddress, const std::string& dbusInterfaceName, const std::string& dbusBusName, const std::string& dbusObjectPath, const std::shared_ptr& dbusConnection, const std::shared_ptr& stub): - TestInterfaceDBusStubAdapterHelper(commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, dbusConnection, std::dynamic_pointer_cast(stub)) { + TestInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, + dbusConnection, std::dynamic_pointer_cast(stub), + NULL) + { + subscribersForTestSelectiveBroadcastSelective_ = std::make_shared(); + subscribersForTestBroadcastWithOutArgsSelective_ = std::make_shared(); +} + +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 clientId) { std::shared_ptr dbusClientId = std::dynamic_pointer_cast(clientId); - if(dbusClientId != NULL) + if(dbusClientId) { - CommonAPI::DBus::DBusMessage dbusMethodCall = dbusClientId->createMessage(getObjectPath(), getInterfaceName(), "TestSelectiveBroadcast"); - getDBusConnection()->sendDBusMessage(dbusMethodCall); + CommonAPI::DBus::DBusStubSignalHelper> + ::sendSignal( + dbusClientId->getDBusId(), + *this, + "TestSelectiveBroadcast", + "" + ); } } -void TestInterfaceDBusStubAdapter::sendTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers) { - const CommonAPI::ClientIdList* actualReceiverList; +void TestInterfaceDBusStubAdapter::sendTestSelectiveBroadcastSelective(const std::shared_ptr receivers) { + std::shared_ptr 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 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 clientId) { - subscribersForTestSelectiveBroadcastSelective_.erase(clientId); + subscribersForTestSelectiveBroadcastSelective_->erase(clientId); stub_->onTestSelectiveBroadcastSelectiveSubscriptionChanged(clientId, CommonAPI::SelectiveBroadcastSubscriptionEvent::UNSUBSCRIBED); } -CommonAPI::ClientIdList* const TestInterfaceDBusStubAdapter::getSubscribersForTestSelectiveBroadcastSelective() { - return &subscribersForTestSelectiveBroadcastSelective_; +std::shared_ptr const TestInterfaceDBusStubAdapter::getSubscribersForTestSelectiveBroadcastSelective() { + return subscribersForTestSelectiveBroadcastSelective_; } static CommonAPI::DBus::DBusMethodWithReplyAdapterDispatcher< @@ -291,24 +314,30 @@ static CommonAPI::DBus::DBusMethodWithReplyAdapterDispatcher< void TestInterfaceDBusStubAdapter::fireTestBroadcastWithOutArgsSelective(const std::shared_ptr clientId, const uint32_t& uint32Value, const std::string& stringValue) { std::shared_ptr dbusClientId = std::dynamic_pointer_cast(clientId); - if(dbusClientId != NULL) + if(dbusClientId) { - CommonAPI::DBus::DBusMessage dbusMethodCall = dbusClientId->createMessage(getObjectPath(), getInterfaceName(), "TestBroadcastWithOutArgs"); - getDBusConnection()->sendDBusMessage(dbusMethodCall); + CommonAPI::DBus::DBusStubSignalHelper> + ::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 receivers) { + std::shared_ptr 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 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 clientId) { - subscribersForTestBroadcastWithOutArgsSelective_.erase(clientId); + subscribersForTestBroadcastWithOutArgsSelective_->erase(clientId); stub_->onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(clientId, CommonAPI::SelectiveBroadcastSubscriptionEvent::UNSUBSCRIBED); } -CommonAPI::ClientIdList* const TestInterfaceDBusStubAdapter::getSubscribersForTestBroadcastWithOutArgsSelective() { - return &subscribersForTestBroadcastWithOutArgsSelective_; +std::shared_ptr 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 +#include #include +#include #undef COMMONAPI_INTERNAL_COMPILATION @@ -27,6 +31,7 @@ typedef CommonAPI::DBus::DBusStubAdapterHelper TestInterfaceD class TestInterfaceDBusStubAdapter: public TestInterfaceStubAdapter, public TestInterfaceDBusStubAdapterHelper { public: TestInterfaceDBusStubAdapter( + const std::shared_ptr& 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& dbusConnection, const std::shared_ptr& 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 clientId); - void sendTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers = NULL); + void sendTestSelectiveBroadcastSelective(const std::shared_ptr receivers = NULL); void subscribeForTestSelectiveBroadcastSelective(const std::shared_ptr clientId, bool& success); void unsubscribeFromTestSelectiveBroadcastSelective(const std::shared_ptr clientId); - CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective(); + std::shared_ptr const getSubscribersForTestSelectiveBroadcastSelective(); void fireTestBroadcastWithOutArgsSelective(const std::shared_ptr 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 receivers = NULL); void subscribeForTestBroadcastWithOutArgsSelective(const std::shared_ptr clientId, bool& success); void unsubscribeFromTestBroadcastWithOutArgsSelective(const std::shared_ptr clientId); - CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective(); + std::shared_ptr 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 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,22 +169,38 @@ 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 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. @@ -331,6 +361,11 @@ bool TestInterfaceProxy<_AttributeExtensions...>::isAvailable() const { return delegate_->isAvailable(); } +template +bool TestInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const { + return delegate_->isAvailableBlocking(); +} + template 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 DefaultAttributeProxyFactoryHelperEvent" methods of the stub should be used. */ virtual void fireTestSelectiveBroadcastSelective(const std::shared_ptr clientId) = 0; - virtual void sendTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers = NULL) = 0; + virtual void sendTestSelectiveBroadcastSelective(const std::shared_ptr receivers = NULL) = 0; virtual void subscribeForTestSelectiveBroadcastSelective(const std::shared_ptr clientId, bool& success) = 0; virtual void unsubscribeFromTestSelectiveBroadcastSelective(const std::shared_ptr clientId) = 0; - virtual CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective() = 0; + virtual std::shared_ptr const getSubscribersForTestSelectiveBroadcastSelective() = 0; /** * Sends a selective broadcast event for TestBroadcastWithOutArgs. Should not be called directly. * Instead, the "fireEvent" methods of the stub should be used. */ virtual void fireTestBroadcastWithOutArgsSelective(const std::shared_ptr 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 receivers = NULL) = 0; virtual void subscribeForTestBroadcastWithOutArgsSelective(const std::shared_ptr clientId, bool& success) = 0; virtual void unsubscribeFromTestBroadcastWithOutArgsSelective(const std::shared_ptr clientId) = 0; - virtual CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective() = 0; + virtual std::shared_ptr 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 subscribersForTestSelectiveBroadcastSelective_; + std::shared_ptr 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 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 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 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 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 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 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 receivers = NULL) = 0; /// retreives the list of all subscribed clients for TestSelectiveBroadcast - virtual CommonAPI::ClientIdList* const getSubscribersForTestSelectiveBroadcastSelective() = 0; + virtual std::shared_ptr const getSubscribersForTestSelectiveBroadcastSelective() = 0; /// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts. virtual void onTestSelectiveBroadcastSelectiveSubscriptionChanged(const std::shared_ptr clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event) = 0; /// Hook method for reacting accepting or denying new subscriptions @@ -160,13 +166,14 @@ class TestInterfaceStub : public CommonAPI::Stub receivers = NULL) = 0; /// retreives the list of all subscribed clients for TestBroadcastWithOutArgs - virtual CommonAPI::ClientIdList* const getSubscribersForTestBroadcastWithOutArgsSelective() = 0; + virtual std::shared_ptr const getSubscribersForTestBroadcastWithOutArgsSelective() = 0; /// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts. virtual void onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(const std::shared_ptr clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event) = 0; /// Hook method for reacting accepting or denying new subscriptions virtual bool onTestBroadcastWithOutArgsSelectiveSubscriptionRequested(const std::shared_ptr 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 @@ -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 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 clientId, uint32_t value) { + setTestPredefinedTypeAttributeAttribute(value); } -bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestPredefinedTypeAttributeAttribute(const std::shared_ptr 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 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 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 clientId, DerivedTypeCollection::TestStructExtended value) { + setTestDerivedStructAttributeAttribute(value); } -bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedStructAttributeAttribute(const std::shared_ptr 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 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 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 clientId, DerivedTypeCollection::TestArrayUInt64 value) { + setTestDerivedArrayAttributeAttribute(value); } -bool TestInterfaceStubDefault::RemoteEventHandler::onRemoteSetTestDerivedArrayAttributeAttribute(const std::shared_ptr 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 clientId, DerivedTypeCollection::TestArrayUInt64 value) { + return onRemoteSetTestDerivedArrayAttributeAttribute(value); +} + void TestInterfaceStubDefault::testEmptyMethod(const std::shared_ptr clientId) { + // Call old style methods in default + testEmptyMethod(); +} +void TestInterfaceStubDefault::testEmptyMethod() { // No operation in default } void TestInterfaceStubDefault::testVoidPredefinedTypeMethod(const std::shared_ptr 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 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 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 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_ptrfireTestPredefinedTypeBroadcastEvent(uint32Value, stringValue); } -void TestInterfaceStubDefault::fireTestSelectiveBroadcastSelective(const CommonAPI::ClientIdList* receivers) { +void TestInterfaceStubDefault::fireTestSelectiveBroadcastSelective(const std::shared_ptr receivers) { stubAdapter_->sendTestSelectiveBroadcastSelective(receivers); } void TestInterfaceStubDefault::onTestSelectiveBroadcastSelectiveSubscriptionChanged(const std::shared_ptr 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 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 receivers) { stubAdapter_->sendTestBroadcastWithOutArgsSelective(uint32Value, stringValue, receivers); } void TestInterfaceStubDefault::onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(const std::shared_ptr 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 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 +#include namespace commonapi { namespace tests { @@ -43,40 +46,46 @@ class TestInterfaceStubDefault : public TestInterfaceStub { virtual void setTestDerivedArrayAttributeAttribute(const std::shared_ptr clientId, DerivedTypeCollection::TestArrayUInt64 value); virtual void testEmptyMethod(const std::shared_ptr clientId); + virtual void testEmptyMethod(); virtual void testVoidPredefinedTypeMethod(const std::shared_ptr clientId, uint32_t uint32Value, std::string stringValue); + virtual void testVoidPredefinedTypeMethod(uint32_t uint32Value, std::string stringValue); virtual void testPredefinedTypeMethod(const std::shared_ptr 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 clientId, DerivedTypeCollection::TestEnumExtended2 testEnumExtended2Value, DerivedTypeCollection::TestMap testMapValue); + virtual void testVoidDerivedTypeMethod(DerivedTypeCollection::TestEnumExtended2 testEnumExtended2Value, DerivedTypeCollection::TestMap testMapValue); virtual void testDerivedTypeMethod(const std::shared_ptr 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 receivers = NULL); + virtual std::shared_ptr const getSubscribersForTestSelectiveBroadcastSelective(); /// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts. virtual void onTestSelectiveBroadcastSelectiveSubscriptionChanged(const std::shared_ptr clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event); /// Hook method for reacting accepting or denying new subscriptions virtual bool onTestSelectiveBroadcastSelectiveSubscriptionRequested(const std::shared_ptr 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 receivers = NULL); + virtual std::shared_ptr const getSubscribersForTestBroadcastWithOutArgsSelective(); /// Hook method for reacting on new subscriptions or removed subscriptions respectively for selective broadcasts. virtual void onTestBroadcastWithOutArgsSelectiveSubscriptionChanged(const std::shared_ptr clientId, const CommonAPI::SelectiveBroadcastSubscriptionEvent event); /// Hook method for reacting accepting or denying new subscriptions virtual bool onTestBroadcastWithOutArgsSelectiveSubscriptionRequested(const std::shared_ptr 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 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 +#include +#include + +#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(enumValue); +} + +inline CommonAPI::OutputStream& operator<<(CommonAPI::OutputStream& outputStream, const BranchInterface::testBranchMethodError& enumValue) { + return outputStream.writeEnumValue(static_cast(enumValue)); +} + +struct BranchInterface::testBranchMethodErrorComparator { + inline bool operator()(const testBranchMethodError& lhs, const testBranchMethodError& rhs) const { + return static_cast(lhs) < static_cast(rhs); + } +}; + + +} // namespace managed +} // namespace tests +} // namespace commonapi + +namespace CommonAPI { + +} + + +namespace std { + //hashes for types + + //hashes for error types + //Hash for testBranchMethodError + template<> + struct hash { + inline size_t operator()(const commonapi::tests::managed::BranchInterface::testBranchMethodError& testBranchMethodError) const { + return static_cast(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 createBranchInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection) { + return std::make_shared(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection); +} + +__attribute__((constructor)) void registerBranchInterfaceDBusProxy(void) { + CommonAPI::DBus::DBusFactory::registerProxyFactoryMethod(BranchInterface::getInterfaceId(), + &createBranchInterfaceDBusProxy); +} + +BranchInterfaceDBusProxy::BranchInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& 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 >::callMethodWithReply( + *this, + "testBranchMethod", + "is", + inInt, inString, + callStatus, + methodError + , outInt, outString); +} +std::future BranchInterfaceDBusProxy::testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback) { + return CommonAPI::DBus::DBusProxyHelper, + CommonAPI::DBus::DBusSerializableArguments >::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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +#include + +namespace commonapi { +namespace tests { +namespace managed { + +class BranchInterfaceDBusProxy: virtual public BranchInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy { + public: + BranchInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& 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 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 + +namespace commonapi { +namespace tests { +namespace managed { + +std::shared_ptr createBranchInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection, + const std::shared_ptr& stubBase) { + return std::make_shared(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& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& stub): + BranchInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, + dbusConnection, std::dynamic_pointer_cast(stub), + NULL) + { +} + +BranchInterfaceDBusStubAdapter::~BranchInterfaceDBusStubAdapter() { + deactivateManagedInstances(); + deinit(); + stub_.reset(); +} + +void BranchInterfaceDBusStubAdapter::deactivateManagedInstances() { +} + +const char* BranchInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const { + static const char* introspectionData = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ; + return introspectionData; +} + + + +static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher< + BranchInterfaceStub, + std::tuple, + std::tuple + > 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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace commonapi { +namespace tests { +namespace managed { + +typedef CommonAPI::DBus::DBusStubAdapterHelper BranchInterfaceDBusStubAdapterHelper; + +class BranchInterfaceDBusStubAdapter: public BranchInterfaceStubAdapter, public BranchInterfaceDBusStubAdapterHelper { + public: + BranchInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& 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 +class BranchInterfaceProxy: virtual public BranchInterface, virtual public BranchInterfaceProxyBase, public _AttributeExtensions... { + public: + BranchInterfaceProxy(std::shared_ptr 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 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 delegate_; +}; + + +// +// BranchInterfaceProxy Implementation +// +template +BranchInterfaceProxy<_AttributeExtensions...>::BranchInterfaceProxy(std::shared_ptr delegate): + delegate_(std::dynamic_pointer_cast(delegate)), + _AttributeExtensions(*(std::dynamic_pointer_cast(delegate)))... { +} + +template +BranchInterfaceProxy<_AttributeExtensions...>::~BranchInterfaceProxy() { +} + +template +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 +std::future BranchInterfaceProxy<_AttributeExtensions...>::testBranchMethodAsync(const int32_t& inInt, const std::string& inString, TestBranchMethodAsyncCallback callback) { + return delegate_->testBranchMethodAsync(inInt, inString, callback); +} + +template +std::string BranchInterfaceProxy<_AttributeExtensions...>::getAddress() const { + return delegate_->getAddress(); +} + +template +const std::string& BranchInterfaceProxy<_AttributeExtensions...>::getDomain() const { + return delegate_->getDomain(); +} + +template +const std::string& BranchInterfaceProxy<_AttributeExtensions...>::getServiceId() const { + return delegate_->getServiceId(); +} + +template +const std::string& BranchInterfaceProxy<_AttributeExtensions...>::getInstanceId() const { + return delegate_->getInstanceId(); +} + +template +bool BranchInterfaceProxy<_AttributeExtensions...>::isAvailable() const { + return delegate_->isAvailable(); +} + +template +bool BranchInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const { + return delegate_->isAvailableBlocking(); +} + +template +CommonAPI::ProxyStatusEvent& BranchInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() { + return delegate_->getProxyStatusEvent(); +} + +template +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 +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace commonapi { +namespace tests { +namespace managed { + +class BranchInterfaceProxyBase: virtual public CommonAPI::Proxy { + public: + + typedef std::function 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 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 + +#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"). + * - an action callback to do local work after the attribute value has been changed + * ("onRemoteChanged"). + * + * 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 { + 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 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 + +namespace commonapi { +namespace tests { +namespace managed { + +BranchInterfaceStubDefault::BranchInterfaceStubDefault(): + remoteEventHandler_(this) { +} + +BranchInterfaceStubRemoteEvent* BranchInterfaceStubDefault::initStubAdapter(const std::shared_ptr& stubAdapter) { + stubAdapter_ = stubAdapter; + return &remoteEventHandler_; +} + + +void BranchInterfaceStubDefault::testBranchMethod(const std::shared_ptr 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 +#include + +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& stubAdapter); + + + virtual void testBranchMethod(const std::shared_ptr 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 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 +#include +#include + +#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(enumValue); +} + +inline CommonAPI::OutputStream& operator<<(CommonAPI::OutputStream& outputStream, const LeafInterface::testLeafMethodError& enumValue) { + return outputStream.writeEnumValue(static_cast(enumValue)); +} + +struct LeafInterface::testLeafMethodErrorComparator { + inline bool operator()(const testLeafMethodError& lhs, const testLeafMethodError& rhs) const { + return static_cast(lhs) < static_cast(rhs); + } +}; + + +} // namespace managed +} // namespace tests +} // namespace commonapi + +namespace CommonAPI { + +} + + +namespace std { + //hashes for types + + //hashes for error types + //Hash for testLeafMethodError + template<> + struct hash { + inline size_t operator()(const commonapi::tests::managed::LeafInterface::testLeafMethodError& testLeafMethodError) const { + return static_cast(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 createLeafInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection) { + return std::make_shared(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection); +} + +__attribute__((constructor)) void registerLeafInterfaceDBusProxy(void) { + CommonAPI::DBus::DBusFactory::registerProxyFactoryMethod(LeafInterface::getInterfaceId(), + &createLeafInterfaceDBusProxy); +} + +LeafInterfaceDBusProxy::LeafInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& 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 >::callMethodWithReply( + *this, + "testLeafMethod", + "is", + inInt, inString, + callStatus, + methodError + , outInt, outString); +} +std::future LeafInterfaceDBusProxy::testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback) { + return CommonAPI::DBus::DBusProxyHelper, + CommonAPI::DBus::DBusSerializableArguments >::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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +#include + +namespace commonapi { +namespace tests { +namespace managed { + +class LeafInterfaceDBusProxy: virtual public LeafInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy { + public: + LeafInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& 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 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 + +namespace commonapi { +namespace tests { +namespace managed { + +std::shared_ptr createLeafInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection, + const std::shared_ptr& stubBase) { + return std::make_shared(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& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& stub): + LeafInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, + dbusConnection, std::dynamic_pointer_cast(stub), + NULL) + { +} + +LeafInterfaceDBusStubAdapter::~LeafInterfaceDBusStubAdapter() { + deactivateManagedInstances(); + deinit(); + stub_.reset(); +} + +void LeafInterfaceDBusStubAdapter::deactivateManagedInstances() { +} + +const char* LeafInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const { + static const char* introspectionData = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ; + return introspectionData; +} + + + +static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher< + LeafInterfaceStub, + std::tuple, + std::tuple + > 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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace commonapi { +namespace tests { +namespace managed { + +typedef CommonAPI::DBus::DBusStubAdapterHelper LeafInterfaceDBusStubAdapterHelper; + +class LeafInterfaceDBusStubAdapter: public LeafInterfaceStubAdapter, public LeafInterfaceDBusStubAdapterHelper { + public: + LeafInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& 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 +class LeafInterfaceProxy: virtual public LeafInterface, virtual public LeafInterfaceProxyBase, public _AttributeExtensions... { + public: + LeafInterfaceProxy(std::shared_ptr 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 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 delegate_; +}; + + +// +// LeafInterfaceProxy Implementation +// +template +LeafInterfaceProxy<_AttributeExtensions...>::LeafInterfaceProxy(std::shared_ptr delegate): + delegate_(std::dynamic_pointer_cast(delegate)), + _AttributeExtensions(*(std::dynamic_pointer_cast(delegate)))... { +} + +template +LeafInterfaceProxy<_AttributeExtensions...>::~LeafInterfaceProxy() { +} + +template +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 +std::future LeafInterfaceProxy<_AttributeExtensions...>::testLeafMethodAsync(const int32_t& inInt, const std::string& inString, TestLeafMethodAsyncCallback callback) { + return delegate_->testLeafMethodAsync(inInt, inString, callback); +} + +template +std::string LeafInterfaceProxy<_AttributeExtensions...>::getAddress() const { + return delegate_->getAddress(); +} + +template +const std::string& LeafInterfaceProxy<_AttributeExtensions...>::getDomain() const { + return delegate_->getDomain(); +} + +template +const std::string& LeafInterfaceProxy<_AttributeExtensions...>::getServiceId() const { + return delegate_->getServiceId(); +} + +template +const std::string& LeafInterfaceProxy<_AttributeExtensions...>::getInstanceId() const { + return delegate_->getInstanceId(); +} + +template +bool LeafInterfaceProxy<_AttributeExtensions...>::isAvailable() const { + return delegate_->isAvailable(); +} + +template +bool LeafInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const { + return delegate_->isAvailableBlocking(); +} + +template +CommonAPI::ProxyStatusEvent& LeafInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() { + return delegate_->getProxyStatusEvent(); +} + +template +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 +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace commonapi { +namespace tests { +namespace managed { + +class LeafInterfaceProxyBase: virtual public CommonAPI::Proxy { + public: + + typedef std::function 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 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 + +#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"). + * - an action callback to do local work after the attribute value has been changed + * ("onRemoteChanged"). + * + * 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 { + 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 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 + +namespace commonapi { +namespace tests { +namespace managed { + +LeafInterfaceStubDefault::LeafInterfaceStubDefault(): + remoteEventHandler_(this) { +} + +LeafInterfaceStubRemoteEvent* LeafInterfaceStubDefault::initStubAdapter(const std::shared_ptr& stubAdapter) { + stubAdapter_ = stubAdapter; + return &remoteEventHandler_; +} + + +void LeafInterfaceStubDefault::testLeafMethod(const std::shared_ptr 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 +#include + +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& stubAdapter); + + + virtual void testLeafMethod(const std::shared_ptr 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 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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include +#include + +#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(enumValue); +} + +inline CommonAPI::OutputStream& operator<<(CommonAPI::OutputStream& outputStream, const RootInterface::testRootMethodError& enumValue) { + return outputStream.writeEnumValue(static_cast(enumValue)); +} + +struct RootInterface::testRootMethodErrorComparator { + inline bool operator()(const testRootMethodError& lhs, const testRootMethodError& rhs) const { + return static_cast(lhs) < static_cast(rhs); + } +}; + + +} // namespace managed +} // namespace tests +} // namespace commonapi + +namespace CommonAPI { + +} + + +namespace std { + //hashes for types + + //hashes for error types + //Hash for testRootMethodError + template<> + struct hash { + inline size_t operator()(const commonapi::tests::managed::RootInterface::testRootMethodError& testRootMethodError) const { + return static_cast(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 createRootInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection) { + return std::make_shared(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection); +} + +__attribute__((constructor)) void registerRootInterfaceDBusProxy(void) { + CommonAPI::DBus::DBusFactory::registerProxyFactoryMethod(RootInterface::getInterfaceId(), + &createRootInterfaceDBusProxy); +} + +RootInterfaceDBusProxy::RootInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& 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 >::callMethodWithReply( + *this, + "testRootMethod", + "is", + inInt, inString, + callStatus, + methodError + , outInt, outString); +} +std::future RootInterfaceDBusProxy::testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback) { + return CommonAPI::DBus::DBusProxyHelper, + CommonAPI::DBus::DBusSerializableArguments >::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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +#include + +namespace commonapi { +namespace tests { +namespace managed { + +class RootInterfaceDBusProxy: virtual public RootInterfaceProxyBase, virtual public CommonAPI::DBus::DBusProxy { + public: + RootInterfaceDBusProxy( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& 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 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 + +namespace commonapi { +namespace tests { +namespace managed { + +std::shared_ptr createRootInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection, + const std::shared_ptr& stubBase) { + return std::make_shared(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& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& stub): + RootInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, + dbusConnection, std::dynamic_pointer_cast(stub), + new CommonAPI::DBus::DBusObjectManagerStub(dbusObjectPath, dbusConnection)) + { +} + +RootInterfaceDBusStubAdapter::~RootInterfaceDBusStubAdapter() { + deactivateManagedInstances(); + deinit(); + stub_.reset(); +} + +void RootInterfaceDBusStubAdapter::deactivateManagedInstances() { + for(std::set::iterator iter = registeredLeafInterfaceInstances.begin(); + iter != registeredLeafInterfaceInstances.end(); ++iter) { + deregisterManagedStubLeafInterface(*iter); + } + for(std::set::iterator iter = registeredBranchInterfaceInstances.begin(); + iter != registeredBranchInterfaceInstances.end(); ++iter) { + deregisterManagedStubBranchInterface(*iter); + } +} + +const char* RootInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const { + static const char* introspectionData = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ; + return introspectionData; +} + + + +static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher< + RootInterfaceStub, + std::tuple, + std::tuple + > 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 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 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& RootInterfaceDBusStubAdapter::getLeafInterfaceInstances() { + return registeredLeafInterfaceInstances; +} + +bool RootInterfaceDBusStubAdapter::registerManagedStubBranchInterface(std::shared_ptr 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 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& 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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace commonapi { +namespace tests { +namespace managed { + +typedef CommonAPI::DBus::DBusStubAdapterHelper RootInterfaceDBusStubAdapterHelper; + +class RootInterfaceDBusStubAdapter: public RootInterfaceStubAdapter, public RootInterfaceDBusStubAdapterHelper { + public: + RootInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& stub); + + ~RootInterfaceDBusStubAdapter(); + + + + bool registerManagedStubLeafInterface(std::shared_ptr, const std::string&); + bool deregisterManagedStubLeafInterface(const std::string&); + std::set& getLeafInterfaceInstances(); + bool registerManagedStubBranchInterface(std::shared_ptr, const std::string&); + bool deregisterManagedStubBranchInterface(const std::string&); + std::set& getBranchInterfaceInstances(); + + const StubDispatcherTable& getStubDispatcherTable(); + + void deactivateManagedInstances(); + + protected: + virtual const char* getMethodsDBusIntrospectionXmlData() const; + + private: + std::set registeredLeafInterfaceInstances; + std::set 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 +class RootInterfaceProxy: virtual public RootInterface, virtual public RootInterfaceProxyBase, public _AttributeExtensions... { + public: + RootInterfaceProxy(std::shared_ptr 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 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 delegate_; +}; + + +// +// RootInterfaceProxy Implementation +// +template +RootInterfaceProxy<_AttributeExtensions...>::RootInterfaceProxy(std::shared_ptr delegate): + delegate_(std::dynamic_pointer_cast(delegate)), + _AttributeExtensions(*(std::dynamic_pointer_cast(delegate)))... { +} + +template +RootInterfaceProxy<_AttributeExtensions...>::~RootInterfaceProxy() { +} + +template +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 +std::future RootInterfaceProxy<_AttributeExtensions...>::testRootMethodAsync(const int32_t& inInt, const std::string& inString, TestRootMethodAsyncCallback callback) { + return delegate_->testRootMethodAsync(inInt, inString, callback); +} + +template +std::string RootInterfaceProxy<_AttributeExtensions...>::getAddress() const { + return delegate_->getAddress(); +} + +template +const std::string& RootInterfaceProxy<_AttributeExtensions...>::getDomain() const { + return delegate_->getDomain(); +} + +template +const std::string& RootInterfaceProxy<_AttributeExtensions...>::getServiceId() const { + return delegate_->getServiceId(); +} + +template +const std::string& RootInterfaceProxy<_AttributeExtensions...>::getInstanceId() const { + return delegate_->getInstanceId(); +} + +template +bool RootInterfaceProxy<_AttributeExtensions...>::isAvailable() const { + return delegate_->isAvailable(); +} + +template +bool RootInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const { + return delegate_->isAvailableBlocking(); +} + +template +CommonAPI::ProxyStatusEvent& RootInterfaceProxy<_AttributeExtensions...>::getProxyStatusEvent() { + return delegate_->getProxyStatusEvent(); +} + +template +CommonAPI::InterfaceVersionAttribute& RootInterfaceProxy<_AttributeExtensions...>::getInterfaceVersionAttribute() { + return delegate_->getInterfaceVersionAttribute(); +} + + +template +CommonAPI::ProxyManager& RootInterfaceProxy<_AttributeExtensions...>::getProxyManagerLeafInterface() { + return delegate_->getProxyManagerLeafInterface(); +} +template +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 +#include + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + + +#include +#include +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace commonapi { +namespace tests { +namespace managed { + +class RootInterfaceProxyBase: virtual public CommonAPI::Proxy { + public: + + typedef std::function 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 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 +#include + +#include "RootInterface.h" + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + + +#include + +#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, const std::string&) = 0; + virtual bool deregisterManagedStubLeafInterface(const std::string&) = 0; + virtual std::set& getLeafInterfaceInstances() = 0; + virtual bool registerManagedStubBranchInterface(std::shared_ptr, const std::string&) = 0; + virtual bool deregisterManagedStubBranchInterface(const std::string&) = 0; + virtual std::set& 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"). + * - an action callback to do local work after the attribute value has been changed + * ("onRemoteChanged"). + * + * 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 { + 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 clientId, int32_t inInt, std::string inString, RootInterface::testRootMethodError& methodError, int32_t& outInt, std::string& outString) = 0; + + virtual bool registerManagedStubLeafInterface(std::shared_ptr, const std::string&) = 0; + virtual bool deregisterManagedStubLeafInterface(const std::string&) = 0; + virtual std::set& getLeafInterfaceInstances() = 0; + virtual bool registerManagedStubBranchInterface(std::shared_ptr, const std::string&) = 0; + virtual bool deregisterManagedStubBranchInterface(const std::string&) = 0; + virtual std::set& 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 + +namespace commonapi { +namespace tests { +namespace managed { + +RootInterfaceStubDefault::RootInterfaceStubDefault(): + autoInstanceCounter_(0), + remoteEventHandler_(this) { +} + +RootInterfaceStubRemoteEvent* RootInterfaceStubDefault::initStubAdapter(const std::shared_ptr& stubAdapter) { + stubAdapter_ = stubAdapter; + return &remoteEventHandler_; +} + + +void RootInterfaceStubDefault::testRootMethod(const std::shared_ptr 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 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 stub, const std::string& instance) { + return stubAdapter_->registerManagedStubLeafInterface(stub, instance); +} +bool RootInterfaceStubDefault::deregisterManagedStubLeafInterface(const std::string& instance) { + return stubAdapter_->deregisterManagedStubLeafInterface(instance); +} +std::set& RootInterfaceStubDefault::getLeafInterfaceInstances() { + return stubAdapter_->getLeafInterfaceInstances(); +} +bool RootInterfaceStubDefault::registerManagedStubBranchInterfaceAutoInstance(std::shared_ptr 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 stub, const std::string& instance) { + return stubAdapter_->registerManagedStubBranchInterface(stub, instance); +} +bool RootInterfaceStubDefault::deregisterManagedStubBranchInterface(const std::string& instance) { + return stubAdapter_->deregisterManagedStubBranchInterface(instance); +} +std::set& 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 +#include + +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& stubAdapter); + + + virtual void testRootMethod(const std::shared_ptr 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); + bool registerManagedStubLeafInterface(std::shared_ptr, const std::string&); + bool deregisterManagedStubLeafInterface(const std::string&); + std::set& getLeafInterfaceInstances(); + bool registerManagedStubBranchInterfaceAutoInstance(std::shared_ptr); + bool registerManagedStubBranchInterface(std::shared_ptr, const std::string&); + bool deregisterManagedStubBranchInterface(const std::string&); + std::set& getBranchInterfaceInstances(); + + protected: + std::shared_ptr 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 createLegacyInterfaceDBusProxy( + const std::shared_ptr& factory, const std::string& commonApiAddress, const std::string& interfaceName, const std::string& busName, const std::string& objectPath, const std::shared_ptr& dbusProxyConnection) { - return std::make_shared(commonApiAddress, interfaceName, busName, objectPath, dbusProxyConnection); + return std::make_shared(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& factory, const std::string& commonApiAddress, const std::string& interfaceName, const std::string& busName, const std::string& objectPath, const std::shared_ptr& dbusProxyconnection): - CommonAPI::DBus::DBusProxy(commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection) - { -} + CommonAPI::DBus::DBusProxy(factory, commonApiAddress, interfaceName, busName, objectPath, dbusProxyconnection) + { + } @@ -91,6 +95,8 @@ std::future 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& 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 TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback); - virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier); virtual std::future OtherTestMethodAsync(OtherTestMethodAsyncCallback callback); - virtual void finish(CommonAPI::CallStatus& callStatus); virtual std::future 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 + +namespace fake { +namespace legacy { +namespace service { + +std::shared_ptr createLegacyInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& interfaceName, + const std::string& busName, + const std::string& objectPath, + const std::shared_ptr& dbusProxyConnection, + const std::shared_ptr& stubBase) { + return std::make_shared(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& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& stub): + LegacyInterfaceDBusStubAdapterHelper(factory, commonApiAddress, dbusInterfaceName, dbusBusName, dbusObjectPath, + dbusConnection, std::dynamic_pointer_cast(stub), + NULL) + { +} + +LegacyInterfaceDBusStubAdapter::~LegacyInterfaceDBusStubAdapter() { + deactivateManagedInstances(); + deinit(); + stub_.reset(); +} + +void LegacyInterfaceDBusStubAdapter::deactivateManagedInstances() { +} + +const char* LegacyInterfaceDBusStubAdapter::getMethodsDBusIntrospectionXmlData() const { + static const char* introspectionData = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ; + return introspectionData; +} + + + +static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher< + LegacyInterfaceStub, + std::tuple, + std::tuple + > testMethodStubDispatcher(&LegacyInterfaceStub::TestMethod, "ii"); +static CommonAPI::DBus::DBusMethodWithReplyStubDispatcher< + LegacyInterfaceStub, + std::tuple<>, + std::tuple + > 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 + +#if !defined (COMMONAPI_INTERNAL_COMPILATION) +#define COMMONAPI_INTERNAL_COMPILATION +#endif + +#include +#include +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace fake { +namespace legacy { +namespace service { + +typedef CommonAPI::DBus::DBusStubAdapterHelper LegacyInterfaceDBusStubAdapterHelper; + +class LegacyInterfaceDBusStubAdapter: public LegacyInterfaceStubAdapter, public LegacyInterfaceDBusStubAdapterHelper { + public: + LegacyInterfaceDBusStubAdapter( + const std::shared_ptr& factory, + const std::string& commonApiAddress, + const std::string& dbusInterfaceName, + const std::string& dbusBusName, + const std::string& dbusObjectPath, + const std::shared_ptr& dbusConnection, + const std::shared_ptr& 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 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 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,22 +91,38 @@ 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 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 std::future LegacyInterfaceProxy<_AttributeExtensions...>::TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback) { return delegate_->TestMethodAsync(input, callback); } - template void LegacyInterfaceProxy<_AttributeExtensions...>::OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier) { delegate_->OtherTestMethod(callStatus, greeting, identifier); @@ -160,7 +171,6 @@ template std::future LegacyInterfaceProxy<_AttributeExtensions...>::OtherTestMethodAsync(OtherTestMethodAsyncCallback callback) { return delegate_->OtherTestMethodAsync(callback); } - template void LegacyInterfaceProxy<_AttributeExtensions...>::finish(CommonAPI::CallStatus& callStatus) { delegate_->finish(callStatus); @@ -171,7 +181,6 @@ std::future LegacyInterfaceProxy<_AttributeExtensions...> return delegate_->finishAsync(callback); } - template std::string LegacyInterfaceProxy<_AttributeExtensions...>::getAddress() const { return delegate_->getAddress(); @@ -197,6 +206,11 @@ bool LegacyInterfaceProxy<_AttributeExtensions...>::isAvailable() const { return delegate_->isAvailable(); } +template +bool LegacyInterfaceProxy<_AttributeExtensions...>::isAvailableBlocking() const { + return delegate_->isAvailableBlocking(); +} + template 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 - #include - #include - - #undef COMMONAPI_INTERNAL_COMPILATION - - namespace fake { - namespace legacy { - namespace service { - - class LegacyInterfaceProxyBase: virtual public CommonAPI::Proxy { - public: - typedef std::function TestMethodAsyncCallback; - typedef std::function OtherTestMethodAsyncCallback; - typedef std::function FinishAsyncCallback; - - - - - virtual void TestMethod(const int32_t& input, CommonAPI::CallStatus& callStatus, int32_t& val1, int32_t& val2) = 0; - virtual std::future TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback) = 0; - - virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier) = 0; - virtual std::future OtherTestMethodAsync(OtherTestMethodAsyncCallback callback) = 0; - - virtual void finish(CommonAPI::CallStatus& callStatus) = 0; - virtual std::future 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 +#include +#include + +#undef COMMONAPI_INTERNAL_COMPILATION + +namespace fake { +namespace legacy { +namespace service { + +class LegacyInterfaceProxyBase: virtual public CommonAPI::Proxy { + public: + + typedef std::function TestMethodAsyncCallback; + typedef std::function OtherTestMethodAsyncCallback; + typedef std::function FinishAsyncCallback; + + + + virtual void TestMethod(const int32_t& input, CommonAPI::CallStatus& callStatus, int32_t& val1, int32_t& val2) = 0; + virtual std::future TestMethodAsync(const int32_t& input, TestMethodAsyncCallback callback) = 0; + virtual void OtherTestMethod(CommonAPI::CallStatus& callStatus, std::string& greeting, int32_t& identifier) = 0; + virtual std::future OtherTestMethodAsync(OtherTestMethodAsyncCallback callback) = 0; + virtual void finish(CommonAPI::CallStatus& callStatus) = 0; + virtual std::future 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 + +#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"). + * - an action callback to do local work after the attribute value has been changed + * ("onRemoteChanged"). + * + * 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 { + 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 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 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 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 + +namespace fake { +namespace legacy { +namespace service { + +LegacyInterfaceStubDefault::LegacyInterfaceStubDefault(): + remoteEventHandler_(this) { +} + +LegacyInterfaceStubRemoteEvent* LegacyInterfaceStubDefault::initStubAdapter(const std::shared_ptr& stubAdapter) { + stubAdapter_ = stubAdapter; + return &remoteEventHandler_; +} + + +void LegacyInterfaceStubDefault::TestMethod(const std::shared_ptr 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 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 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 +#include + +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& stubAdapter); + + + virtual void TestMethod(const std::shared_ptr 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 clientId, std::string& greeting, int32_t& identifier); + virtual void OtherTestMethod(, std::string& greeting, int32_t& identifier); + + virtual void finish(const std::shared_ptr clientId); + virtual void finish(); + + + + + protected: + std::shared_ptr 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.fidl b/src/test/fakeLegacyService.fidl new file mode 100644 index 0000000..6aaa8a8 --- /dev/null +++ b/src/test/fakeLegacyService.fidl @@ -0,0 +1,24 @@ +package fake.legacy.service + +interface LegacyInterface { + version { major 1 minor 0 } + + method TestMethod { + in { + Int32 input + } + out { + Int32 val1 + Int32 val2 + } + } + + method OtherTestMethod { + out { + String greeting + Int32 identifier + } + } + + method finish {} +} diff --git a/src/test/fakeLegacyService/fakeLegacyService.fidl b/src/test/fakeLegacyService/fakeLegacyService.fidl deleted file mode 100644 index 6aaa8a8..0000000 --- a/src/test/fakeLegacyService/fakeLegacyService.fidl +++ /dev/null @@ -1,24 +0,0 @@ -package fake.legacy.service - -interface LegacyInterface { - version { major 1 minor 0 } - - method TestMethod { - in { - Int32 input - } - out { - Int32 val1 - Int32 val2 - } - } - - method OtherTestMethod { - out { - String greeting - Int32 identifier - } - } - - method finish {} -} 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 + } + } + +} + -- cgit v1.2.1