diff options
Diffstat (limited to 'stoc')
111 files changed, 31388 insertions, 0 deletions
diff --git a/stoc/CppunitTest_stoc_dump.mk b/stoc/CppunitTest_stoc_dump.mk new file mode 100644 index 0000000000..98f5fe7f8d --- /dev/null +++ b/stoc/CppunitTest_stoc_dump.mk @@ -0,0 +1,33 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,stoc_dump)) + +$(eval $(call gb_CppunitTest_add_exception_objects,stoc_dump, \ + stoc/test/dump \ +)) + +$(eval $(call gb_CppunitTest_use_internal_api,stoc_dump, \ + cppu_qa_cppumaker_types \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,stoc_dump, \ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_CppunitTest_use_udk_api,stoc_dump)) + +$(eval $(call gb_CppunitTest_use_ure,stoc_dump)) + +$(call gb_CppunitTest_get_target,stoc_dump): \ + UNO_TYPES += $(call gb_UnoApiTarget_get_target,cppu_qa_cppumaker_types) + +# vim: set noet sw=4 ts=4: diff --git a/stoc/CppunitTest_stoc_uriproc.mk b/stoc/CppunitTest_stoc_uriproc.mk new file mode 100644 index 0000000000..f754a02999 --- /dev/null +++ b/stoc/CppunitTest_stoc_uriproc.mk @@ -0,0 +1,32 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,stoc_uriproc)) + +$(eval $(call gb_CppunitTest_add_exception_objects,stoc_uriproc, \ + stoc/test/uriproc/test_uriproc \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,stoc_uriproc, \ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_CppunitTest_use_udk_api,stoc_uriproc)) + +$(eval $(call gb_CppunitTest_use_ure,stoc_uriproc)) + +# In a full build this is already covered indirectly by gb_CppunitTest_use_ure, but a module-only +# build needs it (and instead using gb_CppunitTest_use_component would cause a "duplicate +# implementation" DeploymentException): +$(call gb_CppunitTest_get_target,stoc_uriproc): \ + $(call gb_ComponentTarget_get_target,stoc/util/stocservices) + +# vim: set noet sw=4 ts=4: diff --git a/stoc/IwyuFilter_stoc.yaml b/stoc/IwyuFilter_stoc.yaml new file mode 100644 index 0000000000..5b046e4da1 --- /dev/null +++ b/stoc/IwyuFilter_stoc.yaml @@ -0,0 +1,47 @@ +--- +assumeFilename: stoc/source/javavm/javavm.cxx +excludelist: + stoc/source/implementationregistration/mergekeys.hxx: + # Needed for css shortcut + - sal/types.h + stoc/source/javavm/interact.hxx: + # Base class needs complete type + - com/sun/star/task/XInteractionRequest.hpp + stoc/source/javavm/javavm.hxx: + # Base class needs complete type + - com/sun/star/container/XContainerListener.hpp + - com/sun/star/lang/XInitialization.hpp + - com/sun/star/java/XJavaThreadRegister_11.hpp + - com/sun/star/java/XJavaVM.hpp + - com/sun/star/lang/XServiceInfo.hpp + stoc/source/implementationregistration/mergekeys.cxx: + # Actually used + - com/sun/star/registry/XRegistryKey.hpp + stoc/source/corereflection/crefl.cxx: + # Safer to keep for use in cppu::UnoType template + - com/sun/star/reflection/XTypeDescription.hpp + stoc/source/corereflection/criface.cxx: + # Keep for platform-independent alloca.h abstraction + - sal/alloca.h + # Safer to keep for use in cppu::UnoType template + - com/sun/star/uno/RuntimeException.hpp + stoc/source/invocation_adapterfactory/iafactory.cxx: + # Safer to keep for use in cppu::UnoType template + - com/sun/star/script/XInvocation.hpp + # Complete type needed for uno_Interface + - uno/dispatcher.h + stoc/source/loader/dllcomponentloader.cxx: + # Needed for direct member access + - com/sun/star/lang/XMultiServiceFactory.hpp + stoc/source/security/permissions.cxx: + # Safer to keep for use in cppu::UnoType template + - com/sun/star/security/AllPermission.hpp + # Needed for __DIAGNOSE mode + - vector + - rtl/string.hxx + # Actually used + - com/sun/star/uno/Sequence.hxx + stoc/source/security/access_controller.cxx: + # Needed for __DIAGNOSE mode + - rtl/ustrbuf.hxx + - sal/log.hxx diff --git a/stoc/Library_bootstrap.mk b/stoc/Library_bootstrap.mk new file mode 100644 index 0000000000..62e2360581 --- /dev/null +++ b/stoc/Library_bootstrap.mk @@ -0,0 +1,50 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,bootstrap)) + +ifneq ($(debug),) + +$(eval $(call gb_Library_add_defs,bootstrap,\ + $(if $(diag),\ + $(if $(filter $(diag),cache full),-D__CACHE_DIAGNOSE) \ + $(if $(filter-out $(diag),cache),-D__DIAGNOSE)) \ +)) + +endif + +$(eval $(call gb_Library_use_external,bootstrap,boost_headers)) + +$(eval $(call gb_Library_use_internal_bootstrap_api,bootstrap,\ + udkapi \ +)) + +$(eval $(call gb_Library_use_libraries,bootstrap,\ + cppu \ + cppuhelper \ + reg \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_set_componentfile,bootstrap,stoc/util/bootstrap,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,bootstrap,\ + stoc/source/defaultregistry/defaultregistry \ + stoc/source/implementationregistration/implreg \ + stoc/source/implementationregistration/mergekeys \ + stoc/source/loader/dllcomponentloader \ + stoc/source/security/access_controller \ + stoc/source/security/file_policy \ + stoc/source/security/permissions \ + stoc/source/servicemanager/servicemanager \ + stoc/source/simpleregistry/simpleregistry \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_introspection.mk b/stoc/Library_introspection.mk new file mode 100644 index 0000000000..58ce9e404d --- /dev/null +++ b/stoc/Library_introspection.mk @@ -0,0 +1,29 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,introspection)) + +$(eval $(call gb_Library_use_external,introspection,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,introspection)) + +$(eval $(call gb_Library_use_libraries,introspection,\ + cppu \ + cppuhelper \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_set_componentfile,introspection,stoc/source/inspect/introspection,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,introspection,\ + stoc/source/inspect/introspection \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_invocadapt.mk b/stoc/Library_invocadapt.mk new file mode 100644 index 0000000000..8166cfadcb --- /dev/null +++ b/stoc/Library_invocadapt.mk @@ -0,0 +1,28 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,invocadapt)) + +$(eval $(call gb_Library_use_external,invocadapt,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,invocadapt)) + +$(eval $(call gb_Library_use_libraries,invocadapt,\ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_set_componentfile,invocadapt,stoc/source/invocation_adapterfactory/invocadapt,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,invocadapt,\ + stoc/source/invocation_adapterfactory/iafactory \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_invocation.mk b/stoc/Library_invocation.mk new file mode 100644 index 0000000000..d62de01f41 --- /dev/null +++ b/stoc/Library_invocation.mk @@ -0,0 +1,28 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,invocation)) + +$(eval $(call gb_Library_use_external,invocation,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,invocation)) + +$(eval $(call gb_Library_use_libraries,invocation,\ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_set_componentfile,invocation,stoc/source/invocation/invocation,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,invocation,\ + stoc/source/invocation/invocation \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_javaloader.mk b/stoc/Library_javaloader.mk new file mode 100644 index 0000000000..2fd3a491f8 --- /dev/null +++ b/stoc/Library_javaloader.mk @@ -0,0 +1,30 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,javaloader)) + +$(eval $(call gb_Library_use_external,javaloader,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,javaloader)) + +$(eval $(call gb_Library_use_libraries,javaloader,\ + cppu \ + cppuhelper \ + jvmaccess \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_set_componentfile,javaloader,stoc/source/javaloader/javaloader,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,javaloader,\ + stoc/source/javaloader/javaloader \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_javavm.mk b/stoc/Library_javavm.mk new file mode 100644 index 0000000000..adcb49691c --- /dev/null +++ b/stoc/Library_javavm.mk @@ -0,0 +1,40 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,javavm)) + +$(eval $(call gb_Library_use_external,javavm,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,javavm)) + +$(eval $(call gb_Library_use_libraries,javavm,\ + cppu \ + cppuhelper \ + i18nlangtag \ + jvmaccess \ + jvmfwk \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_set_componentfile,javavm,stoc/source/javavm/javavm,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,javavm,\ + stoc/source/javavm/interact \ + stoc/source/javavm/javavm \ + stoc/source/javavm/jvmargs \ +)) + +ifeq ($(OS),MACOSX) +$(eval $(call gb_Library_use_system_darwin_frameworks,javavm,\ + CoreFoundation \ +)) +endif + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_namingservice.mk b/stoc/Library_namingservice.mk new file mode 100644 index 0000000000..6822635f00 --- /dev/null +++ b/stoc/Library_namingservice.mk @@ -0,0 +1,28 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,namingservice)) + +$(eval $(call gb_Library_use_external,namingservice,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,namingservice)) + +$(eval $(call gb_Library_use_libraries,namingservice,\ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_set_componentfile,namingservice,stoc/source/namingservice/namingservice,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,namingservice,\ + stoc/source/namingservice/namingservice \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_proxyfac.mk b/stoc/Library_proxyfac.mk new file mode 100644 index 0000000000..9cd7be33a3 --- /dev/null +++ b/stoc/Library_proxyfac.mk @@ -0,0 +1,26 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,proxyfac)) + +$(eval $(call gb_Library_use_udk_api,proxyfac)) + +$(eval $(call gb_Library_use_libraries,proxyfac,\ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_set_componentfile,proxyfac,stoc/source/proxy_factory/proxyfac,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,proxyfac,\ + stoc/source/proxy_factory/proxyfac \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_reflection.mk b/stoc/Library_reflection.mk new file mode 100644 index 0000000000..083b237836 --- /dev/null +++ b/stoc/Library_reflection.mk @@ -0,0 +1,34 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,reflection)) + +$(eval $(call gb_Library_use_external,reflection,boost_headers)) + +$(eval $(call gb_Library_use_udk_api,reflection)) + +$(eval $(call gb_Library_use_libraries,reflection,\ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_set_componentfile,reflection,stoc/source/corereflection/reflection,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,reflection,\ + stoc/source/corereflection/crarray \ + stoc/source/corereflection/crbase \ + stoc/source/corereflection/crcomp \ + stoc/source/corereflection/crefl \ + stoc/source/corereflection/crenum \ + stoc/source/corereflection/criface \ + stoc/source/corereflection/dump \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Library_stocservices.mk b/stoc/Library_stocservices.mk new file mode 100644 index 0000000000..4d276129d8 --- /dev/null +++ b/stoc/Library_stocservices.mk @@ -0,0 +1,34 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Library_Library,stocservices)) + +$(eval $(call gb_Library_use_udk_api,stocservices)) + +$(eval $(call gb_Library_use_libraries,stocservices,\ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_use_external,stocservices,boost_headers)) + +$(eval $(call gb_Library_set_componentfile,stocservices,stoc/util/stocservices,ure/services)) + +$(eval $(call gb_Library_add_exception_objects,stocservices,\ + stoc/source/typeconv/convert \ + stoc/source/uriproc/ExternalUriReferenceTranslator \ + stoc/source/uriproc/UriReference \ + stoc/source/uriproc/UriReferenceFactory \ + stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand \ + stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript \ + stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/Makefile b/stoc/Makefile new file mode 100644 index 0000000000..ccb1c85a04 --- /dev/null +++ b/stoc/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/stoc/Module_stoc.mk b/stoc/Module_stoc.mk new file mode 100644 index 0000000000..e94e5be476 --- /dev/null +++ b/stoc/Module_stoc.mk @@ -0,0 +1,37 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# 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/. +# + +$(eval $(call gb_Module_Module,stoc)) + +$(eval $(call gb_Module_add_targets,stoc,\ + Library_bootstrap \ + Library_introspection \ + Library_invocadapt \ + Library_invocation \ + Library_namingservice \ + Library_proxyfac \ + Library_reflection \ + Library_stocservices \ +)) + +ifeq ($(ENABLE_JAVA),TRUE) + +$(eval $(call gb_Module_add_targets,stoc,\ + Library_javaloader \ + Library_javavm \ +)) + +endif + +$(eval $(call gb_Module_add_check_targets,stoc, \ + CppunitTest_stoc_dump \ + CppunitTest_stoc_uriproc \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/stoc/README.md b/stoc/README.md new file mode 100644 index 0000000000..47fd36d20f --- /dev/null +++ b/stoc/README.md @@ -0,0 +1,39 @@ +# Registries, Reflection, Introspection Implementation for UNO + +The UNO types and services bootstrapping code is very old, and concepts +are tightly knit together. Whenever you want to change something you risk +backwards incompatibility. The code causes mental pain, and whenever +you need to touch it you want to run away screaming. One typically ends +up doing minimally invasive changes. That way, you have a chance of +surviving the process. But you also pile up guilt. + +At the heart of the matter there is the old binary "store" file structure +and the `XRegistry` interface on top of it. At runtime, both all the UNO +type information (scattered across a number of binary `.rdb` files) and +all the UNO service information (scattered across a number of `.rdb` files +that used to be binary but have been mostly changed to XML now) are +represented by a single `XRegistry` instance each. + +The way the respective information is represented in the `XRegistry` +interface simply corresponds to the way the information is stored in the +binary `.rdb` files. Those files are designed for storage of hierarchically +nested small blobs of information. Hence, for example information about +a UNO interface type `com.sun.star.foo.XBar` is stored in a nested "folder" +with path `com - sun - star - foo - XBar`, containing little blobs of +information about the type's ancestors, its methods, etc. Similarly +for information about instantiable services like `com.sun.star.baz.Boz`. + +As there are typically multiple `.rdb` files containing types resp. +services (URE specific, LO specific, from extensions, ...), but they need +to be represented by a single `XRegistry` instance, so "nested registries" +were invented. They effectively form a linear list of chaining `XRegistry` +instances together. Whenever a path needs to be looked up in the top-level +registry, it effectively searches through the linear list of nested +registries. All with the cumbersome UNO `XRegistry` interface between +the individual parts. Horror. + +When the XML service `.rdb`s were introduced, we chickened out (see above +for rationale) and put them behind an `XRegistry` facade, so that they +would seamlessly integrate with the existing mess. We postponed +systematic clean-up to the pie-in-the-sky days of LibreOffice 4 (or, "once we'll +become incompatible with OpenOffice.org," as the phrase used to be back then) diff --git a/stoc/source/corereflection/base.hxx b/stoc/source/corereflection/base.hxx new file mode 100644 index 0000000000..03d844de3c --- /dev/null +++ b/stoc/source/corereflection/base.hxx @@ -0,0 +1,406 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +// #define TEST_LIST_CLASSES + +#ifndef INCLUDED_STOC_SOURCE_COREREFLECTION_BASE_HXX +#define INCLUDED_STOC_SOURCE_COREREFLECTION_BASE_HXX + +#include <sal/config.h> + +#include <o3tl/any.hxx> +#include <osl/mutex.hxx> +#include <uno/mapping.hxx> +#include <uno/dispatcher.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/compbase.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ref.hxx> + +#include "lrucache.hxx" + +#ifdef TEST_LIST_CLASSES +#include <vector> +#include <algorithm> +#endif +#include <unordered_map> +#include <memory> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> + +#include <com/sun/star/reflection/XIdlReflection.hpp> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::reflection { class XIdlClass; } +namespace com::sun::star::reflection { class XIdlField; } +namespace com::sun::star::reflection { class XIdlMethod; } + +namespace stoc_corefl +{ + +#ifdef TEST_LIST_CLASSES +extern std::vector<OUString> g_aClassNames; +#endif + + +::osl::Mutex & getMutexAccess(); + + +inline bool td_equals( typelib_TypeDescription * pTD, typelib_TypeDescriptionReference * pType ) +{ + return (pTD->pWeakRef == pType || + (pTD->pTypeName->length == pType->pTypeName->length && + rtl_ustr_compare( pTD->pTypeName->buffer, pType->pTypeName->buffer ) == 0)); +} + +typedef std::unordered_map< OUString, css::uno::WeakReference< css::reflection::XIdlField > > OUString2Field; +typedef std::unordered_map< OUString, css::uno::WeakReference< css::reflection::XIdlMethod > > OUString2Method; + + +class IdlReflectionServiceImpl + : public ::cppu::WeakComponentImplHelper< + css::reflection::XIdlReflection, + css::container::XHierarchicalNameAccess, + css::lang::XServiceInfo> +{ + ::osl::Mutex _aComponentMutex; + css::uno::Reference< css::container::XHierarchicalNameAccess > _xTDMgr; + + // caching + LRU_CacheAnyByOUString _aElements; + + css::uno::Mapping _aCpp2Uno; + css::uno::Mapping _aUno2Cpp; + + inline css::uno::Reference< css::reflection::XIdlClass > constructClass( typelib_TypeDescription * pTypeDescr ); + +public: + /// @throws css::uno::RuntimeException + const css::uno::Mapping & getCpp2Uno(); + /// @throws css::uno::RuntimeException + const css::uno::Mapping & getUno2Cpp(); + /// @throws css::uno::RuntimeException + uno_Interface * mapToUno( const css::uno::Any & rObj, typelib_InterfaceTypeDescription * pTo ); + + // ctor/ dtor + explicit IdlReflectionServiceImpl( const css::uno::Reference< css::uno::XComponentContext > & xContext ); + virtual ~IdlReflectionServiceImpl() override; + + // WeakComponentImplHelper + virtual void SAL_CALL disposing() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XIdlReflection + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL forName( const OUString & rTypeName ) override; + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getType( const css::uno::Any & rObj ) override; + + // XHierarchicalNameAccess + virtual css::uno::Any SAL_CALL getByHierarchicalName( const OUString & rName ) override; + virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString & rName ) override; + + /// @throws css::uno::RuntimeException + css::uno::Reference< css::reflection::XIdlClass > forType( typelib_TypeDescription * pTypeDescr ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::reflection::XIdlClass > forType( typelib_TypeDescriptionReference * pRef ); +}; + + +class IdlClassImpl + : public ::cppu::WeakImplHelper< css::reflection::XIdlClass > +{ + rtl::Reference<IdlReflectionServiceImpl> + m_xReflection; + + OUString _aName; + css::uno::TypeClass _eTypeClass; + + typelib_TypeDescription * _pTypeDescr; + +public: + typelib_TypeDescription * getTypeDescr() const + { return _pTypeDescr; } + IdlReflectionServiceImpl * getReflection() const + { return m_xReflection.get(); } + + // Ctor + IdlClassImpl( IdlReflectionServiceImpl * pReflection, + OUString aName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ); + virtual ~IdlClassImpl() override; + + // XIdlClassImpl default implementation + virtual css::uno::TypeClass SAL_CALL getTypeClass() override; + virtual OUString SAL_CALL getName() override; + virtual sal_Bool SAL_CALL equals( const css::uno::Reference< css::reflection::XIdlClass >& xType ) override; + + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual void SAL_CALL createObject( css::uno::Any & rObj ) override; + + // def impl ???? + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getClasses() override; + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getClass( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getInterfaces() override; + + // structs, interfaces + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getSuperclasses() override; + // structs + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; + // interfaces + virtual css::uno::Uik SAL_CALL getUik() override; + virtual css::uno::Reference< css::reflection::XIdlMethod > SAL_CALL getMethod( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlMethod > > SAL_CALL getMethods() override; + // array + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getComponentType() override; + virtual css::uno::Reference< css::reflection::XIdlArray > SAL_CALL getArray() override; +}; + + +class InterfaceIdlClassImpl + : public IdlClassImpl +{ + typedef std::pair< OUString, typelib_TypeDescription * > MemberInit; + + css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > _xSuperClasses; + + std::unique_ptr<MemberInit[]> _pSortedMemberInit; // first methods, then attributes + OUString2Field _aName2Field; + OUString2Method _aName2Method; + sal_Int32 _nMethods; + sal_Int32 _nAttributes; + + void initMembers(); + +public: + typelib_InterfaceTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_InterfaceTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor/ dtor + InterfaceIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + , _nMethods( 0 ) + , _nAttributes( 0 ) + {} + virtual ~InterfaceIdlClassImpl() override; + + // IdlClassImpl modifications + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getSuperclasses() override; + virtual css::uno::Uik SAL_CALL getUik() override; + virtual css::uno::Reference< css::reflection::XIdlMethod > SAL_CALL getMethod( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlMethod > > SAL_CALL getMethods() override; + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; + virtual void SAL_CALL createObject( css::uno::Any & rObj ) override; +}; + + +class CompoundIdlClassImpl + : public IdlClassImpl +{ + css::uno::Reference< css::reflection::XIdlClass > + _xSuperClass; + std::optional< css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > > + m_xFields; + OUString2Field _aName2Field; + +public: + typelib_CompoundTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_CompoundTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor/ dtor + CompoundIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + {} + virtual ~CompoundIdlClassImpl() override; + + // IdlClassImpl modifications + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getSuperclasses() override; + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; +}; + + +typedef cppu::ImplInheritanceHelper<IdlClassImpl, css::reflection::XIdlArray> ArrayIdlClassImpl_Base; +class ArrayIdlClassImpl : public ArrayIdlClassImpl_Base +{ +public: + typelib_IndirectTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_IndirectTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor + ArrayIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : ArrayIdlClassImpl_Base( pReflection, rName, eTypeClass, pTypeDescr ) + {} + + // IdlClassImpl modifications + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getComponentType() override; + virtual css::uno::Reference< css::reflection::XIdlArray > SAL_CALL getArray() override; + + // XIdlArray + virtual void SAL_CALL realloc( css::uno::Any & rArray, sal_Int32 nLen ) override; + virtual sal_Int32 SAL_CALL getLen( const css::uno::Any & rArray ) override; + virtual css::uno::Any SAL_CALL get( const css::uno::Any & rArray, sal_Int32 nIndex ) override; + virtual void SAL_CALL set( css::uno::Any & rArray, sal_Int32 nIndex, const css::uno::Any & rNewValue ) override; +}; + + +class EnumIdlClassImpl + : public IdlClassImpl +{ + std::optional< css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > > m_xFields; + OUString2Field _aName2Field; + +public: + typelib_EnumTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_EnumTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor/ dtor + EnumIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + {} + virtual ~EnumIdlClassImpl() override; + + // IdlClassImpl modifications + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; + virtual void SAL_CALL createObject( css::uno::Any & rObj ) override; +}; + + +class IdlMemberImpl + : public ::cppu::WeakImplHelper< css::reflection::XIdlMember > +{ + rtl::Reference<IdlReflectionServiceImpl> + m_xReflection; + OUString _aName; + + typelib_TypeDescription * _pTypeDescr; + typelib_TypeDescription * _pDeclTypeDescr; + +protected: + css::uno::Reference< css::reflection::XIdlClass > _xDeclClass; + +public: + IdlReflectionServiceImpl * getReflection() const + { return m_xReflection.get(); } + typelib_TypeDescription * getTypeDescr() const + { return _pTypeDescr; } + typelib_TypeDescription * getDeclTypeDescr() const + { return _pDeclTypeDescr; } + + // ctor/ dtor + IdlMemberImpl( IdlReflectionServiceImpl * pReflection, OUString aName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr ); + virtual ~IdlMemberImpl() override; + + // XIdlMember + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; +}; + + +// coerces to type descr pTo else queries for it: the interface pointer is returned via rDest +// ## type to XidlClass coercion possible +inline bool extract( + const css::uno::Any & rObj, typelib_InterfaceTypeDescription * pTo, + css::uno::Reference< css::uno::XInterface > & rDest, + IdlReflectionServiceImpl * pRefl ) +{ + rDest.clear(); + if (nullptr != pTo) + { + if (! rObj.hasValue()) + return true; + if (rObj.getValueTypeClass() == css::uno::TypeClass_INTERFACE) + { + return ::uno_type_assignData( + &rDest, pTo->aBase.pWeakRef, + const_cast< void * >( rObj.getValue() ), rObj.getValueTypeRef(), + reinterpret_cast< uno_QueryInterfaceFunc >(css::uno::cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(css::uno::cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(css::uno::cpp_release) ); + } + else if (auto t = o3tl::tryAccess<css::uno::Type>(rObj)) + { + rDest = pRefl->forType( t->getTypeLibType() ); + return rDest.is(); + } + } + return false; +} + +inline bool coerce_assign( + void * pDest, typelib_TypeDescription * pTD, const css::uno::Any & rSource, + IdlReflectionServiceImpl * pRefl ) +{ + if (pTD->eTypeClass == typelib_TypeClass_INTERFACE) + { + css::uno::Reference< css::uno::XInterface > xVal; + if (extract( rSource, reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD), xVal, pRefl )) + { + if (*static_cast<css::uno::XInterface **>(pDest)) + (*static_cast<css::uno::XInterface **>(pDest))->release(); + *static_cast<css::uno::XInterface **>(pDest) = xVal.get(); + if (*static_cast<css::uno::XInterface **>(pDest)) + (*static_cast<css::uno::XInterface **>(pDest))->acquire(); + return true; + } + return false; + } + else if (pTD->eTypeClass == typelib_TypeClass_ANY) + { + return uno_assignData( + pDest, pTD, + const_cast<css::uno::Any *>(&rSource), pTD, + reinterpret_cast< uno_QueryInterfaceFunc >(css::uno::cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(css::uno::cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(css::uno::cpp_release) ); + } + else + { + return uno_type_assignData( + pDest, pTD->pWeakRef, + const_cast<void *>(rSource.getValue()), rSource.getValueTypeRef(), + reinterpret_cast< uno_QueryInterfaceFunc >(css::uno::cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(css::uno::cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(css::uno::cpp_release) ); + } +} + +} + + +#endif // INCLUDED_STOC_SOURCE_COREREFLECTION_BASE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crarray.cxx b/stoc/source/corereflection/crarray.cxx new file mode 100644 index 0000000000..67be31e1ac --- /dev/null +++ b/stoc/source/corereflection/crarray.cxx @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <typelib/typedescription.h> +#include <uno/data.h> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include "base.hxx" + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +// XIdlArray + +void ArrayIdlClassImpl::realloc( Any & rArray, sal_Int32 nLen ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + getXWeak(), 0 ); + } + if (nLen < 0) + { + throw IllegalArgumentException( + "negative length given!", + getXWeak(), 1 ); + } + + uno_Sequence ** ppSeq = const_cast<uno_Sequence **>(static_cast<uno_Sequence * const *>(rArray.getValue())); + uno_sequence_realloc( ppSeq, &getTypeDescr()->aBase, + nLen, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + rArray.pData = ppSeq; +} + +sal_Int32 ArrayIdlClassImpl::getLen( const Any & rArray ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + getXWeak(), 0 ); + } + + return (*static_cast<uno_Sequence * const *>(rArray.getValue()))->nElements; +} + +Any ArrayIdlClassImpl::get( const Any & rArray, sal_Int32 nIndex ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + getXWeak(), 0 ); + } + + uno_Sequence * pSeq = *static_cast<uno_Sequence * const *>(rArray.getValue()); + if (pSeq->nElements <= nIndex) + { + throw ArrayIndexOutOfBoundsException( + "illegal index given, index " + OUString::number(nIndex) + " is < " + OUString::number(pSeq->nElements), + getXWeak() ); + } + + Any aRet; + typelib_TypeDescription * pElemTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pElemTypeDescr, getTypeDescr()->pType ); + uno_any_destruct( &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_construct( &aRet, &pSeq->elements[nIndex * pElemTypeDescr->nSize], + pElemTypeDescr, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + TYPELIB_DANGER_RELEASE( pElemTypeDescr ); + return aRet; +} + + +void ArrayIdlClassImpl::set( Any & rArray, sal_Int32 nIndex, const Any & rNewValue ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + getXWeak(), 0 ); + } + + uno_Sequence * pSeq = *static_cast<uno_Sequence * const *>(rArray.getValue()); + if (pSeq->nElements <= nIndex) + { + throw ArrayIndexOutOfBoundsException( + "illegal index given, index " + OUString::number(nIndex) + " is < " + OUString::number(pSeq->nElements), + getXWeak() ); + } + + uno_Sequence ** ppSeq = const_cast<uno_Sequence **>(static_cast<uno_Sequence * const *>(rArray.getValue())); + uno_sequence_reference2One( + ppSeq, &getTypeDescr()->aBase, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + rArray.pData = ppSeq; + pSeq = *ppSeq; + + typelib_TypeDescription * pElemTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pElemTypeDescr, getTypeDescr()->pType ); + + if (! coerce_assign( &pSeq->elements[nIndex * pElemTypeDescr->nSize], + pElemTypeDescr, rNewValue, getReflection() )) + { + TYPELIB_DANGER_RELEASE( pElemTypeDescr ); + throw IllegalArgumentException( + "sequence element is not assignable by given value!", + getXWeak(), 2 ); + } + TYPELIB_DANGER_RELEASE( pElemTypeDescr ); +} + +// ArrayIdlClassImpl + +sal_Bool ArrayIdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + return (xType.is() && + (equals( xType ) || + (xType->getTypeClass() == getTypeClass() && // must be sequence|array + getComponentType()->isAssignableFrom( xType->getComponentType() )))); +} + +Reference< XIdlClass > ArrayIdlClassImpl::getComponentType() +{ + return getReflection()->forType( getTypeDescr()->pType ); +} + +Reference< XIdlArray > ArrayIdlClassImpl::getArray() +{ + return this; +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crbase.cxx b/stoc/source/corereflection/crbase.cxx new file mode 100644 index 0000000000..439ee5b222 --- /dev/null +++ b/stoc/source/corereflection/crbase.cxx @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <uno/any2.h> + +#include <utility> + +#include "base.hxx" + +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +#ifdef TEST_LIST_CLASSES +ClassNameVector g_aClassNames; +#endif + + +::osl::Mutex & getMutexAccess() +{ + static osl::Mutex s_aMutex; + + return s_aMutex; +} + + +IdlClassImpl::IdlClassImpl( IdlReflectionServiceImpl * pReflection, + OUString aName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : m_xReflection( pReflection ) + , _aName(std::move( aName )) + , _eTypeClass( static_cast<TypeClass>(eTypeClass) ) + , _pTypeDescr( pTypeDescr ) +{ + if (_pTypeDescr) + { + typelib_typedescription_acquire( _pTypeDescr ); + if (! _pTypeDescr->bComplete) + typelib_typedescription_complete( &_pTypeDescr ); + } + +#ifdef TEST_LIST_CLASSES + ClassNameVector::const_iterator iFind( std::find( g_aClassNames.begin(), g_aClassNames.end(), _aName ) ); + OSL_ENSURE( iFind == g_aClassNames.end(), "### idl class already exists!" ); + g_aClassNames.insert(g_aClassNames.begin(), _aName); +#endif +} + +IdlClassImpl::~IdlClassImpl() +{ + if (_pTypeDescr) + typelib_typedescription_release( _pTypeDescr ); + m_xReflection.clear(); + +#ifdef TEST_LIST_CLASSES + ClassNameVector::iterator iFind( std::find( g_aClassNames.begin(), g_aClassNames.end(), _aName ) ); + OSL_ENSURE( iFind != g_aClassNames.end(), "### idl class does not exist!" ); + g_aClassNames.erase( iFind ); +#endif +} + +// XIdlClassImpl default implementation + +TypeClass IdlClassImpl::getTypeClass() +{ + return _eTypeClass; +} + +OUString IdlClassImpl::getName() +{ + return _aName; +} + +sal_Bool IdlClassImpl::equals( const Reference< XIdlClass >& xType ) +{ + return (xType.is() && + (xType->getTypeClass() == _eTypeClass) && (xType->getName() == _aName)); +} + +const bool s_aAssignableFromTab[11][11] = +{ + /* from CH, BO, BY, SH, US, LO, UL, HY, UH, FL, DO */ +/* TypeClass_CHAR */ { true, false, false, false, false, false, false, false, false, false, false }, +/* TypeClass_BOOLEAN */ { false, true, false, false, false, false, false, false, false, false, false }, +/* TypeClass_BYTE */ { false, false, true, false, false, false, false, false, false, false, false }, +/* TypeClass_SHORT */ { false, false, true, true, true, false, false, false, false, false, false }, +/* TypeClass_UNSIGNED_SHORT */ { false, false, true, true, true, false, false, false, false, false, false }, +/* TypeClass_LONG */ { false, false, true, true, true, true, true, false, false, false, false }, +/* TypeClass_UNSIGNED_LONG */ { false, false, true, true, true, true, true, false, false, false, false }, +/* TypeClass_HYPER */ { false, false, true, true, true, true, true, true, true, false, false }, +/* TypeClass_UNSIGNED_HYPER */ { false, false, true, true, true, true, true, true, true, false, false }, +/* TypeClass_FLOAT */ { false, false, true, true, true, true, true, true, true, true, false }, +/* TypeClass_DOUBLE */ { false, false, true, true, true, true, true, true, true, true, true } +}; + +sal_Bool IdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + TypeClass eAssign = getTypeClass(); + if (equals( xType ) || eAssign == TypeClass_ANY) // default shot + { + return true; + } + else + { + TypeClass eFrom = xType->getTypeClass(); + if (eAssign > TypeClass_VOID && eAssign < TypeClass_STRING && + eFrom > TypeClass_VOID && eFrom < TypeClass_STRING) + { + return s_aAssignableFromTab[static_cast<int>(eAssign)-1][static_cast<int>(eFrom)-1]; + } + } + return false; +} + +void IdlClassImpl::createObject( Any & rObj ) +{ + rObj.clear(); + uno_any_destruct( &rObj, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_construct( &rObj, nullptr, getTypeDescr(), nullptr ); +} + +// what TODO ???? + +Sequence< Reference< XIdlClass > > IdlClassImpl::getClasses() +{ + OSL_FAIL( "### unexpected use!" ); + return Sequence< Reference< XIdlClass > >(); +} + +Reference< XIdlClass > IdlClassImpl::getClass( const OUString & ) +{ + OSL_FAIL( "### unexpected use!" ); + return Reference< XIdlClass >(); +} + +Sequence< Reference< XIdlClass > > IdlClassImpl::getInterfaces() +{ +// OSL_FAIL( "### unexpected use!" ); + return Sequence< Reference< XIdlClass > >(); +} + +// structs, interfaces + +Sequence< Reference< XIdlClass > > IdlClassImpl::getSuperclasses() +{ + return Sequence< Reference< XIdlClass > >(); +} +// structs + +Reference< XIdlField > IdlClassImpl::getField( const OUString & ) +{ + return Reference< XIdlField >(); +} + +Sequence< Reference< XIdlField > > IdlClassImpl::getFields() +{ + return Sequence< Reference< XIdlField > >(); +} +// interfaces + +Uik IdlClassImpl::getUik() +{ + return Uik(); +} + +Reference< XIdlMethod > IdlClassImpl::getMethod( const OUString & ) +{ + return Reference< XIdlMethod >(); +} + +Sequence< Reference< XIdlMethod > > IdlClassImpl::getMethods() +{ + return Sequence< Reference< XIdlMethod > >(); +} +// array + +Reference< XIdlClass > IdlClassImpl::getComponentType() +{ + return Reference< XIdlClass >(); +} + +Reference< XIdlArray > IdlClassImpl::getArray() +{ + return Reference< XIdlArray >(); +} + + +IdlMemberImpl::IdlMemberImpl( IdlReflectionServiceImpl * pReflection, OUString aName, + typelib_TypeDescription * pTypeDescr, + typelib_TypeDescription * pDeclTypeDescr ) + : m_xReflection( pReflection ) + , _aName(std::move( aName )) + , _pTypeDescr( pTypeDescr ) + , _pDeclTypeDescr( pDeclTypeDescr ) +{ + typelib_typedescription_acquire( _pTypeDescr ); + if (! _pTypeDescr->bComplete) + typelib_typedescription_complete( &_pTypeDescr ); + typelib_typedescription_acquire( _pDeclTypeDescr ); + if (! _pDeclTypeDescr->bComplete) + typelib_typedescription_complete( &_pDeclTypeDescr ); +} + +IdlMemberImpl::~IdlMemberImpl() +{ + typelib_typedescription_release( _pDeclTypeDescr ); + typelib_typedescription_release( _pTypeDescr ); +} + +// XIdlMember + +Reference< XIdlClass > IdlMemberImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + Reference< XIdlClass > xDeclClass( getReflection()->forType( getDeclTypeDescr() ) ); + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + _xDeclClass = xDeclClass; + } + return _xDeclClass; +} + +OUString IdlMemberImpl::getName() +{ + return _aName; +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crcomp.cxx b/stoc/source/corereflection/crcomp.cxx new file mode 100644 index 0000000000..b1143e158b --- /dev/null +++ b/stoc/source/corereflection/crcomp.cxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include <com/sun/star/reflection/XIdlField.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/uno/TypeClass.hpp> + +#include "base.hxx" + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +namespace { + +typedef cppu::ImplInheritanceHelper<IdlMemberImpl, XIdlField, XIdlField2> IdlCompFieldImpl_Base; +class IdlCompFieldImpl : public IdlCompFieldImpl_Base +{ + sal_Int32 _nOffset; + +public: + IdlCompFieldImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr, + sal_Int32 nOffset ) + : IdlCompFieldImpl_Base( pReflection, rName, pTypeDescr, pDeclTypeDescr ) + , _nOffset( nOffset ) + {} + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlField + virtual Reference< XIdlClass > SAL_CALL getType() override; + virtual FieldAccessMode SAL_CALL getAccessMode() override; + virtual Any SAL_CALL get( const Any & rObj ) override; + virtual void SAL_CALL set( const Any & rObj, const Any & rValue ) override; + // XIdlField2: getType, getAccessMode and get are equal to XIdlField + virtual void SAL_CALL set( Any & rObj, const Any & rValue ) override; +}; + +} + +// XIdlMember + +Reference< XIdlClass > IdlCompFieldImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + { + typelib_CompoundTypeDescription * pTD = + reinterpret_cast<typelib_CompoundTypeDescription *>(getDeclTypeDescr()); + while (pTD) + { + typelib_TypeDescriptionReference ** ppTypeRefs = pTD->ppTypeRefs; + for ( sal_Int32 nPos = pTD->nMembers; nPos--; ) + { + if (td_equals( getTypeDescr(), ppTypeRefs[nPos] )) + { + _xDeclClass = getReflection()->forType( &pTD->aBase ); + return _xDeclClass; + } + } + pTD = pTD->pBaseTypeDescription; + } + } + } + return _xDeclClass; +} + +OUString IdlCompFieldImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlField + +Reference< XIdlClass > IdlCompFieldImpl::getType() +{ + return getReflection()->forType( getTypeDescr() ); +} + +FieldAccessMode IdlCompFieldImpl::getAccessMode() +{ + return FieldAccessMode_READWRITE; +} + +Any IdlCompFieldImpl::get( const Any & rObj ) +{ + if (rObj.getValueTypeClass() == css::uno::TypeClass_STRUCT || + rObj.getValueTypeClass() == css::uno::TypeClass_EXCEPTION) + { + typelib_TypeDescription * pObjTD = nullptr; + TYPELIB_DANGER_GET( &pObjTD, rObj.getValueTypeRef() ); + + typelib_TypeDescription * pTD = pObjTD; + typelib_TypeDescription * pDeclTD = getDeclTypeDescr(); + while (pTD && !typelib_typedescription_equals( pTD, pDeclTD )) + pTD = &reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->pBaseTypeDescription->aBase; + + OSL_ENSURE( pTD, "### illegal object type!" ); + if (pTD) + { + TYPELIB_DANGER_RELEASE( pObjTD ); + Any aRet; + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_construct( + &aRet, const_cast<char *>(static_cast<char const *>(rObj.getValue()) + _nOffset), getTypeDescr(), + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + return aRet; + } + TYPELIB_DANGER_RELEASE( pObjTD ); + } + throw IllegalArgumentException( + "expected struct or exception, got " + rObj.getValueType().getTypeName(), + getXWeak(), 0 ); +} + +void IdlCompFieldImpl::set( const Any & rObj, const Any & rValue ) +{ + if (rObj.getValueTypeClass() == css::uno::TypeClass_STRUCT || + rObj.getValueTypeClass() == css::uno::TypeClass_EXCEPTION) + { + typelib_TypeDescription * pObjTD = nullptr; + TYPELIB_DANGER_GET( &pObjTD, rObj.getValueTypeRef() ); + + typelib_TypeDescription * pTD = pObjTD; + typelib_TypeDescription * pDeclTD = getDeclTypeDescr(); + while (pTD && !typelib_typedescription_equals( pTD, pDeclTD )) + pTD = &reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->pBaseTypeDescription->aBase; + + OSL_ENSURE( pTD, "### illegal object type!" ); + if (pTD) + { + TYPELIB_DANGER_RELEASE( pObjTD ); + if (!coerce_assign( const_cast<char *>(static_cast<char const *>(rObj.getValue()) + _nOffset), getTypeDescr(), rValue, getReflection() )) + { + throw IllegalArgumentException( + "cannot assign value to destination", + getXWeak(), 1 ); + } + return; + } + TYPELIB_DANGER_RELEASE( pObjTD ); + } + throw IllegalArgumentException( + "expected struct or exception, got " + rObj.getValueType().getTypeName(), + getXWeak(), 0 ); +} + + +void IdlCompFieldImpl::set( Any & rObj, const Any & rValue ) +{ + if (rObj.getValueTypeClass() == css::uno::TypeClass_STRUCT || + rObj.getValueTypeClass() == css::uno::TypeClass_EXCEPTION) + { + typelib_TypeDescription * pObjTD = nullptr; + TYPELIB_DANGER_GET( &pObjTD, rObj.getValueTypeRef() ); + + typelib_TypeDescription * pTD = pObjTD; + typelib_TypeDescription * pDeclTD = getDeclTypeDescr(); + while (pTD && !typelib_typedescription_equals( pTD, pDeclTD )) + pTD = &reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->pBaseTypeDescription->aBase; + + OSL_ENSURE( pTD, "### illegal object type!" ); + if (pTD) + { + TYPELIB_DANGER_RELEASE( pObjTD ); + if (!coerce_assign( const_cast<char *>(static_cast<char const *>(rObj.getValue()) + _nOffset), getTypeDescr(), rValue, getReflection() )) + { + throw IllegalArgumentException( + "cannot assign to destination", + getXWeak(), 1 ); + } + return; + } + TYPELIB_DANGER_RELEASE( pObjTD ); + } + throw IllegalArgumentException( + "expected struct or exception, got " + rObj.getValueType().getTypeName(), + getXWeak(), 0 ); +} + + +CompoundIdlClassImpl::~CompoundIdlClassImpl() +{ +} + + +sal_Bool CompoundIdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + if (xType.is()) + { + TypeClass eTC = xType->getTypeClass(); + if (eTC == TypeClass_STRUCT || eTC == TypeClass_EXCEPTION) + { + if (equals( xType )) + return true; + else + { + const Sequence< Reference< XIdlClass > > & rSeq = xType->getSuperclasses(); + if (rSeq.hasElements()) + { + OSL_ENSURE( rSeq.getLength() == 1, "### unexpected len of super classes!" ); + return isAssignableFrom( rSeq[0] ); + } + } + } + } + return false; +} + +Sequence< Reference< XIdlClass > > CompoundIdlClassImpl::getSuperclasses() +{ + if (! _xSuperClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xSuperClass.is()) + { + typelib_CompoundTypeDescription * pCompTypeDescr = getTypeDescr()->pBaseTypeDescription; + if (pCompTypeDescr) + _xSuperClass = getReflection()->forType( &pCompTypeDescr->aBase ); + } + } + if (_xSuperClass.is()) + return Sequence< Reference< XIdlClass > >( &_xSuperClass, 1 ); + else + return Sequence< Reference< XIdlClass > >(); +} + +Reference< XIdlField > CompoundIdlClassImpl::getField( const OUString & rName ) +{ + if (! m_xFields) + getFields(); // init fields + + const OUString2Field::const_iterator iFind( _aName2Field.find( rName ) ); + if (iFind != _aName2Field.end()) + return Reference< XIdlField >( (*iFind).second ); + else + return Reference< XIdlField >(); +} + +Sequence< Reference< XIdlField > > CompoundIdlClassImpl::getFields() +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xFields) + { + sal_Int32 nAll = 0; + typelib_CompoundTypeDescription * pCompTypeDescr = getTypeDescr(); + for ( ; pCompTypeDescr; pCompTypeDescr = pCompTypeDescr->pBaseTypeDescription ) + nAll += pCompTypeDescr->nMembers; + + Sequence< Reference< XIdlField > > aFields( nAll ); + Reference< XIdlField > * pSeq = aFields.getArray(); + + for ( pCompTypeDescr = getTypeDescr(); pCompTypeDescr; + pCompTypeDescr = pCompTypeDescr->pBaseTypeDescription ) + { + typelib_TypeDescriptionReference ** ppTypeRefs = pCompTypeDescr->ppTypeRefs; + rtl_uString ** ppNames = pCompTypeDescr->ppMemberNames; + sal_Int32 * pMemberOffsets = pCompTypeDescr->pMemberOffsets; + + for ( sal_Int32 nPos = pCompTypeDescr->nMembers; nPos--; ) + { + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, ppTypeRefs[nPos] ); + OSL_ENSURE( pTD, "### cannot get field in struct!" ); + if (pTD) + { + OUString aName( ppNames[nPos] ); + _aName2Field[aName] = pSeq[--nAll] = new IdlCompFieldImpl( + getReflection(), aName, pTD, IdlClassImpl::getTypeDescr(), pMemberOffsets[nPos] ); + TYPELIB_DANGER_RELEASE( pTD ); + } + } + } + + m_xFields = std::move( aFields ); + } + return *m_xFields; +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crefl.cxx b/stoc/source/corereflection/crefl.cxx new file mode 100644 index 0000000000..1f4562181a --- /dev/null +++ b/stoc/source/corereflection/crefl.cxx @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/reflection/XConstantTypeDescription.hpp> +#include <com/sun/star/reflection/XTypeDescription.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <o3tl/any.hxx> +#include <uno/lbnames.h> +#include "base.hxx" + +using namespace css; +using namespace css::uno; +using namespace css::lang; +using namespace css::reflection; +using namespace cppu; +using namespace osl; + +namespace stoc_corefl +{ + +IdlReflectionServiceImpl::IdlReflectionServiceImpl( + const Reference< XComponentContext > & xContext ) + : WeakComponentImplHelper( _aComponentMutex ) +{ + xContext->getValueByName( + "/singletons/com.sun.star.reflection.theTypeDescriptionManager" ) >>= _xTDMgr; + OSL_ENSURE( _xTDMgr.is(), "### cannot get singleton \"TypeDescriptionManager\" from context!" ); +} + +IdlReflectionServiceImpl::~IdlReflectionServiceImpl() {} + +// XComponent + +void IdlReflectionServiceImpl::disposing() +{ + MutexGuard aGuard( _aComponentMutex ); + _aElements.clear(); +#ifdef TEST_LIST_CLASSES + OSL_ENSURE( g_aClassNames.empty(), "### idl classes still alive!" ); + for (auto const& className : g_aClassNames) + { + OUString aName(className); + } +#endif +} + +// XServiceInfo + +OUString IdlReflectionServiceImpl::getImplementationName() +{ + return "com.sun.star.comp.stoc.CoreReflection"; +} + +sal_Bool IdlReflectionServiceImpl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > IdlReflectionServiceImpl::getSupportedServiceNames() +{ + Sequence< OUString > seqNames { "com.sun.star.reflection.CoreReflection" }; + return seqNames; +} + +// XIdlReflection + +Reference< XIdlClass > IdlReflectionServiceImpl::getType( const Any & rObj ) +{ + return (rObj.hasValue() ? forType( rObj.getValueTypeRef() ) : Reference< XIdlClass >()); +} + + +inline Reference< XIdlClass > IdlReflectionServiceImpl::constructClass( + typelib_TypeDescription * pTypeDescr ) +{ + OSL_ENSURE( pTypeDescr->eTypeClass != typelib_TypeClass_TYPEDEF, "### unexpected typedef!" ); + + switch (pTypeDescr->eTypeClass) + { + case typelib_TypeClass_VOID: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_STRING: + case typelib_TypeClass_ANY: + return new IdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_ENUM: + return new EnumIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + return new CompoundIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_SEQUENCE: + return new ArrayIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_INTERFACE: + return new InterfaceIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_TYPE: + return new IdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + default: + SAL_INFO("stoc", "corereflection type unsupported: " << pTypeDescr->pTypeName); + return Reference< XIdlClass >(); + } +} + +Reference< XIdlClass > IdlReflectionServiceImpl::forName( const OUString & rTypeName ) +{ + Reference< XIdlClass > xRet; + Any aAny( _aElements.getValue( rTypeName ) ); + + if (aAny.hasValue()) + { + aAny >>= xRet; + } + else + { + // try to get _type_ by name + typelib_TypeDescription * pTD = nullptr; + typelib_typedescription_getByName( &pTD, rTypeName.pData ); + if (pTD) + { + xRet = constructClass( pTD ); + if (xRet.is()) + _aElements.setValue( rTypeName, Any( xRet ) ); // update + typelib_typedescription_release( pTD ); + } + } + + return xRet; +} + +// XHierarchicalNameAccess + +Any IdlReflectionServiceImpl::getByHierarchicalName( const OUString & rName ) +{ + Any aRet( _aElements.getValue( rName ) ); + if (! aRet.hasValue()) + { + aRet = _xTDMgr->getByHierarchicalName( rName ); + if (aRet.getValueTypeClass() == TypeClass_INTERFACE) + { + // type retrieved from tdmgr + OSL_ASSERT( (*o3tl::forceAccess<Reference<XInterface>>(aRet))->queryInterface( + cppu::UnoType<XTypeDescription>::get()).hasValue() ); + + css::uno::Reference< css::reflection::XConstantTypeDescription > + ctd; + if (aRet >>= ctd) + { + aRet = ctd->getConstantValue(); + } + else + { + // if you are interested in a type then CALL forName()!!! + // this way is NOT recommended for types, because this method looks for constants first + + // if td manager found some type, it will be in the cache (hopefully... we just got it) + // so the second retrieving via c typelib callback chain should succeed... + + // try to get _type_ by name + typelib_TypeDescription * pTD = nullptr; + typelib_typedescription_getByName( &pTD, rName.pData ); + + aRet.clear(); // kick XTypeDescription interface + + if (pTD) + { + Reference< XIdlClass > xIdlClass( constructClass( pTD ) ); + aRet.setValue( &xIdlClass, cppu::UnoType<XIdlClass>::get()); + typelib_typedescription_release( pTD ); + } + } + } + // else is enum member(?) + + // update + if (!aRet.hasValue()) + throw container::NoSuchElementException( rName ); + + _aElements.setValue( rName, aRet ); + } + return aRet; +} + +sal_Bool IdlReflectionServiceImpl::hasByHierarchicalName( const OUString & rName ) +{ + try + { + return getByHierarchicalName( rName ).hasValue(); + } + catch (container::NoSuchElementException &) + { + } + return false; +} + + +Reference< XIdlClass > IdlReflectionServiceImpl::forType( typelib_TypeDescription * pTypeDescr ) +{ + Reference< XIdlClass > xRet; + OUString aName( pTypeDescr->pTypeName ); + Any aAny( _aElements.getValue( aName ) ); + + if (aAny.hasValue()) + { + aAny >>= xRet; + } + else + { + xRet = constructClass( pTypeDescr ); + if (xRet.is()) + _aElements.setValue( aName, Any( xRet ) ); // update + } + + return xRet; +} + +Reference< XIdlClass > IdlReflectionServiceImpl::forType( typelib_TypeDescriptionReference * pRef ) +{ + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, pRef ); + if (pTD) + { + Reference< XIdlClass > xRet = forType( pTD ); + TYPELIB_DANGER_RELEASE( pTD ); + return xRet; + } + throw RuntimeException( + "IdlReflectionServiceImpl::forType() failed!", + getXWeak() ); +} + + +const Mapping & IdlReflectionServiceImpl::getCpp2Uno() +{ + if (! _aCpp2Uno.is()) + { + MutexGuard aGuard( getMutexAccess() ); + if (! _aCpp2Uno.is()) + { + _aCpp2Uno = Mapping( CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO ); + OSL_ENSURE( _aCpp2Uno.is(), "### cannot get c++ to uno mapping!" ); + if (! _aCpp2Uno.is()) + { + throw RuntimeException( + "cannot get c++ to uno mapping!", + getXWeak() ); + } + } + } + return _aCpp2Uno; +} + +const Mapping & IdlReflectionServiceImpl::getUno2Cpp() +{ + if (! _aUno2Cpp.is()) + { + MutexGuard aGuard( getMutexAccess() ); + if (! _aUno2Cpp.is()) + { + _aUno2Cpp = Mapping( UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME ); + OSL_ENSURE( _aUno2Cpp.is(), "### cannot get uno to c++ mapping!" ); + if (! _aUno2Cpp.is()) + { + throw RuntimeException( + "cannot get uno to c++ mapping!", + getXWeak() ); + } + } + } + return _aUno2Cpp; +} + +uno_Interface * IdlReflectionServiceImpl::mapToUno( + const Any & rObj, typelib_InterfaceTypeDescription * pTo ) +{ + Reference< XInterface > xObj; + if (extract( rObj, pTo, xObj, this )) + return static_cast<uno_Interface *>(getCpp2Uno().mapInterface( xObj.get(), pTo )); + + throw RuntimeException( + "illegal object given!", + getXWeak() ); +} + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_CoreReflection_get_implementation( + css::uno::XComponentContext * context, + css::uno::Sequence<css::uno::Any> const & arguments) +{ + SAL_WARN_IF( + arguments.hasElements(), "stoc", "unexpected singleton arguments"); + return cppu::acquire(new stoc_corefl::IdlReflectionServiceImpl(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crenum.cxx b/stoc/source/corereflection/crenum.cxx new file mode 100644 index 0000000000..2cb07c9b5e --- /dev/null +++ b/stoc/source/corereflection/crenum.cxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "base.hxx" + +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include <com/sun/star/reflection/XIdlField2.hpp> + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +namespace { + +typedef cppu::ImplInheritanceHelper<IdlMemberImpl, XIdlField, XIdlField2> IdlEnumFieldImpl_Base; +class IdlEnumFieldImpl : public IdlEnumFieldImpl_Base +{ + sal_Int32 _nValue; + +public: + IdlEnumFieldImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, sal_Int32 nValue ) + : IdlEnumFieldImpl_Base( pReflection, rName, pTypeDescr, pTypeDescr ) + , _nValue( nValue ) + {} + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlField + virtual Reference< XIdlClass > SAL_CALL getType() override; + virtual FieldAccessMode SAL_CALL getAccessMode() override; + virtual Any SAL_CALL get( const Any & rObj ) override; + virtual void SAL_CALL set( const Any & rObj, const Any & rValue ) override; + // XIdlField2: getType, getAccessMode and get are equal to XIdlField + virtual void SAL_CALL set( Any & rObj, const Any & rValue ) override; +}; + +} + +// XIdlMember + +Reference< XIdlClass > IdlEnumFieldImpl::getDeclaringClass() +{ + return IdlMemberImpl::getDeclaringClass(); +} + +OUString IdlEnumFieldImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlField + +Reference< XIdlClass > IdlEnumFieldImpl::getType() +{ + return getDeclaringClass(); +} + +FieldAccessMode IdlEnumFieldImpl::getAccessMode() +{ + return FieldAccessMode_READONLY; +} + +Any IdlEnumFieldImpl::get( const Any & ) +{ + return Any( &_nValue, getTypeDescr() ); +} + +void IdlEnumFieldImpl::set( const Any &, const Any & ) +{ + throw IllegalAccessException( + "cannot set enum field, it is constant", + getXWeak() ); +} + +void IdlEnumFieldImpl::set( Any &, const Any & ) +{ + throw IllegalAccessException( + "cannot set enum field, it is constant", + getXWeak() ); +} + + +EnumIdlClassImpl::~EnumIdlClassImpl() +{ +} + +// IdlClassImpl modifications + +Reference< XIdlField > EnumIdlClassImpl::getField( const OUString & rName ) +{ + if (! m_xFields) + getFields(); // init members + + const OUString2Field::const_iterator iFind( _aName2Field.find( rName ) ); + if (iFind != _aName2Field.end()) + return (*iFind).second; + else + return Reference< XIdlField >(); +} + +Sequence< Reference< XIdlField > > EnumIdlClassImpl::getFields() +{ + if (! m_xFields) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xFields) + { + sal_Int32 nFields = getTypeDescr()->nEnumValues; + Sequence< Reference< XIdlField > > aFields( nFields ); + Reference< XIdlField > * pSeq = aFields.getArray(); + + while (nFields--) + { + OUString aName( getTypeDescr()->ppEnumNames[nFields] ); + _aName2Field[aName] = pSeq[nFields] = new IdlEnumFieldImpl( + getReflection(), aName, IdlClassImpl::getTypeDescr(), getTypeDescr()->pEnumValues[nFields] ); + } + + m_xFields = std::move( aFields ); + } + } + return *m_xFields; +} + +void EnumIdlClassImpl::createObject( Any & rObj ) +{ + sal_Int32 eVal = + reinterpret_cast<typelib_EnumTypeDescription *>(IdlClassImpl::getTypeDescr())->nDefaultEnumValue; + rObj.setValue( &eVal, IdlClassImpl::getTypeDescr() ); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/criface.cxx b/stoc/source/corereflection/criface.cxx new file mode 100644 index 0000000000..23d3d9bae2 --- /dev/null +++ b/stoc/source/corereflection/criface.cxx @@ -0,0 +1,830 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <limits> + +#ifdef SAL_UNX +#include <sal/alloca.h> +#endif +#include <o3tl/any.hxx> +#include <typelib/typedescription.hxx> +#include <uno/data.h> + +#include "base.hxx" + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/typeprovider.hxx> + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace { + +std::size_t multipleOf16(std::size_t n) { + assert(n <= std::numeric_limits<std::size_t>::max() - 15); + return (n + 15) & ~std::size_t(15); +} + +} + +namespace stoc_corefl +{ + +namespace { + +typedef cppu::ImplInheritanceHelper<IdlMemberImpl, XIdlField, XIdlField2> IdlAttributeFieldImpl_Base; +class IdlAttributeFieldImpl : public IdlAttributeFieldImpl_Base +{ +public: + typelib_InterfaceAttributeTypeDescription * getAttributeTypeDescr() const + { return reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>(getTypeDescr()); } + + IdlAttributeFieldImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr ) + : IdlAttributeFieldImpl_Base( pReflection, rName, pTypeDescr, pDeclTypeDescr ) + {} + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlField + virtual Reference< XIdlClass > SAL_CALL getType() override; + virtual FieldAccessMode SAL_CALL getAccessMode() override; + virtual Any SAL_CALL get( const Any & rObj ) override; + virtual void SAL_CALL set( const Any & rObj, const Any & rValue ) override; + // XIdlField2: getType, getAccessMode and get are equal to XIdlField + virtual void SAL_CALL set( Any & rObj, const Any & rValue ) override; + +private: + void checkException( + uno_Any * exception, Reference< XInterface > const & context) const; +}; + +} + +// XIdlMember + +Reference< XIdlClass > IdlAttributeFieldImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + { + OUString aName(getAttributeTypeDescr()->aBase.aBase.pTypeName); + sal_Int32 i = aName.indexOf(':'); + OSL_ASSERT(i >= 0); + _xDeclClass = getReflection()->forName(aName.copy(0, i)); + } + } + return _xDeclClass; +} + +OUString IdlAttributeFieldImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlField + +Reference< XIdlClass > IdlAttributeFieldImpl::getType() +{ + return getReflection()->forType( + getAttributeTypeDescr()->pAttributeTypeRef ); +} + +FieldAccessMode IdlAttributeFieldImpl::getAccessMode() +{ + return (getAttributeTypeDescr()->bReadOnly + ? FieldAccessMode_READONLY : FieldAccessMode_READWRITE); +} + +Any IdlAttributeFieldImpl::get( const Any & rObj ) +{ + uno_Interface * pUnoI = getReflection()->mapToUno( + rObj, reinterpret_cast<typelib_InterfaceTypeDescription *>(getDeclTypeDescr()) ); + OSL_ENSURE( pUnoI, "### illegal destination object given!" ); + if (pUnoI) + { + TypeDescription aTD( getAttributeTypeDescr()->pAttributeTypeRef ); + typelib_TypeDescription * pTD = aTD.get(); + + uno_Any aExc; + uno_Any * pExc = &aExc; + void * pReturn = alloca( pTD->nSize ); + + (*pUnoI->pDispatcher)( pUnoI, getTypeDescr(), pReturn, nullptr, &pExc ); + (*pUnoI->release)( pUnoI ); + + checkException(pExc, *o3tl::doAccess<Reference<XInterface>>(rObj)); + Any aRet; + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_constructAndConvert( &aRet, pReturn, pTD, getReflection()->getUno2Cpp().get() ); + uno_destructData( pReturn, pTD, nullptr ); + return aRet; + } + throw IllegalArgumentException( + "illegal object given!", + getXWeak(), 0 ); +} + +void IdlAttributeFieldImpl::set( Any & rObj, const Any & rValue ) +{ + if (getAttributeTypeDescr()->bReadOnly) + { + throw IllegalAccessException( + "cannot set readonly attribute!", + getXWeak() ); + } + + uno_Interface * pUnoI = getReflection()->mapToUno( + rObj, reinterpret_cast<typelib_InterfaceTypeDescription *>(getDeclTypeDescr()) ); + OSL_ENSURE( pUnoI, "### illegal destination object given!" ); + if (pUnoI) + { + TypeDescription aTD( getAttributeTypeDescr()->pAttributeTypeRef ); + typelib_TypeDescription * pTD = aTD.get(); + + // construct uno value to be set + void * pArgs[1]; + void * pArg = pArgs[0] = alloca( pTD->nSize ); + + bool bAssign; + if (pTD->eTypeClass == typelib_TypeClass_ANY) + { + uno_copyAndConvertData( pArg, const_cast< Any * >(&rValue), + pTD, getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (typelib_typedescriptionreference_equals( rValue.getValueTypeRef(), pTD->pWeakRef )) + { + uno_copyAndConvertData( pArg, const_cast< void * >(rValue.getValue()), + pTD, getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (pTD->eTypeClass == typelib_TypeClass_INTERFACE) + { + Reference< XInterface > xObj; + bAssign = extract( + rValue, reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD), xObj, + getReflection() ); + if (bAssign) + { + *static_cast<void **>(pArg) = getReflection()->getCpp2Uno().mapInterface( + xObj.get(), reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD) ); + } + } + else + { + typelib_TypeDescription * pValueTD = nullptr; + TYPELIB_DANGER_GET( &pValueTD, rValue.getValueTypeRef() ); + // construct temp uno val to do proper assignment: todo opt + void * pTemp = alloca( pValueTD->nSize ); + uno_copyAndConvertData( + pTemp, const_cast<void *>(rValue.getValue()), pValueTD, getReflection()->getCpp2Uno().get() ); + uno_constructData( + pArg, pTD ); + // assignment does simple conversion + bAssign = uno_assignData( + pArg, pTD, pTemp, pValueTD, nullptr, nullptr, nullptr ); + uno_destructData( + pTemp, pValueTD, nullptr ); + TYPELIB_DANGER_RELEASE( pValueTD ); + } + + if (bAssign) + { + uno_Any aExc; + uno_Any * pExc = &aExc; + (*pUnoI->pDispatcher)( pUnoI, getTypeDescr(), nullptr, pArgs, &pExc ); + (*pUnoI->release)( pUnoI ); + + uno_destructData( pArg, pTD, nullptr ); + checkException(pExc, *o3tl::doAccess<Reference<XInterface>>(rObj)); + return; + } + (*pUnoI->release)( pUnoI ); + + throw IllegalArgumentException( + "illegal value given!", + *o3tl::doAccess<Reference<XInterface>>(rObj), 1 ); + } + throw IllegalArgumentException( + "illegal destination object given!", + getXWeak(), 0 ); +} + +void IdlAttributeFieldImpl::set( const Any & rObj, const Any & rValue ) +{ + IdlAttributeFieldImpl::set( const_cast< Any & >( rObj ), rValue ); +} + +void IdlAttributeFieldImpl::checkException( + uno_Any * exception, Reference< XInterface > const & context) const +{ + if (exception == nullptr) + return; + + Any e; + uno_any_destruct(&e, reinterpret_cast< uno_ReleaseFunc >(cpp_release)); + uno_type_any_constructAndConvert( + &e, exception->pData, exception->pType, + getReflection()->getUno2Cpp().get()); + uno_any_destruct(exception, nullptr); + if (!e.isExtractableTo( + cppu::UnoType<RuntimeException>::get())) + { + throw WrappedTargetRuntimeException( + "non-RuntimeException occurred when accessing an" + " interface type attribute", + context, e); + } + cppu::throwException(e); +} + +namespace { + +typedef cppu::ImplInheritanceHelper<IdlMemberImpl, XIdlMethod> IdlInterfaceMethodImpl_Base; +class IdlInterfaceMethodImpl : public IdlInterfaceMethodImpl_Base +{ + std::optional<Sequence< Reference< XIdlClass > >> m_xExceptionTypes; + std::optional<Sequence< Reference< XIdlClass > >> m_xParamTypes; + std::optional<Sequence< ParamInfo >> m_xParamInfos; + +public: + typelib_InterfaceMethodTypeDescription * getMethodTypeDescr() const + { return reinterpret_cast<typelib_InterfaceMethodTypeDescription *>(getTypeDescr()); } + + IdlInterfaceMethodImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr ) + : IdlInterfaceMethodImpl_Base( pReflection, rName, pTypeDescr, pDeclTypeDescr ) + {} + + // XTypeProvider + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlMethod + virtual Reference< XIdlClass > SAL_CALL getReturnType() override; + virtual Sequence< Reference< XIdlClass > > SAL_CALL getParameterTypes() override; + virtual Sequence< ParamInfo > SAL_CALL getParameterInfos() override; + virtual Sequence< Reference< XIdlClass > > SAL_CALL getExceptionTypes() override; + virtual MethodMode SAL_CALL getMode() override; + virtual Any SAL_CALL invoke( const Any & rObj, Sequence< Any > & rArgs ) override; +}; + +} + +// XTypeProvider + +Sequence< sal_Int8 > IdlInterfaceMethodImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XIdlMember + +Reference< XIdlClass > IdlInterfaceMethodImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + { + OUString aName(getMethodTypeDescr()->aBase.aBase.pTypeName); + sal_Int32 i = aName.indexOf(':'); + OSL_ASSERT(i >= 0); + _xDeclClass = getReflection()->forName(aName.copy(0, i)); + } + } + return _xDeclClass; +} + +OUString IdlInterfaceMethodImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlMethod + +Reference< XIdlClass > SAL_CALL IdlInterfaceMethodImpl::getReturnType() +{ + return getReflection()->forType( getMethodTypeDescr()->pReturnTypeRef ); +} + +Sequence< Reference< XIdlClass > > IdlInterfaceMethodImpl::getExceptionTypes() +{ + if (! m_xExceptionTypes) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xExceptionTypes) + { + sal_Int32 nExc = getMethodTypeDescr()->nExceptions; + Sequence< Reference< XIdlClass > > aTempExceptionTypes( nExc ); + Reference< XIdlClass > * pExceptionTypes = aTempExceptionTypes.getArray(); + + typelib_TypeDescriptionReference ** ppExc = + getMethodTypeDescr()->ppExceptions; + IdlReflectionServiceImpl * pRefl = getReflection(); + + while (nExc--) + pExceptionTypes[nExc] = pRefl->forType( ppExc[nExc] ); + + m_xExceptionTypes = std::move(aTempExceptionTypes); + } + } + return *m_xExceptionTypes; +} + +Sequence< Reference< XIdlClass > > IdlInterfaceMethodImpl::getParameterTypes() +{ + if (! m_xParamTypes) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xParamTypes) + { + sal_Int32 nParams = getMethodTypeDescr()->nParams; + Sequence< Reference< XIdlClass > > aTempParamTypes( nParams ); + Reference< XIdlClass > * pParamTypes = aTempParamTypes.getArray(); + + typelib_MethodParameter * pTypelibParams = + getMethodTypeDescr()->pParams; + IdlReflectionServiceImpl * pRefl = getReflection(); + + while (nParams--) + pParamTypes[nParams] = pRefl->forType( pTypelibParams[nParams].pTypeRef ); + + m_xParamTypes = std::move(aTempParamTypes); + } + } + return *m_xParamTypes; +} + +Sequence< ParamInfo > IdlInterfaceMethodImpl::getParameterInfos() +{ + if (! m_xParamInfos) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xParamInfos) + { + sal_Int32 nParams = getMethodTypeDescr()->nParams; + Sequence< ParamInfo > aTempParamInfos( nParams ); + ParamInfo * pParamInfos = aTempParamInfos.getArray(); + + typelib_MethodParameter * pTypelibParams = + getMethodTypeDescr()->pParams; + + if (m_xParamTypes) // use param types + { + const Reference< XIdlClass > * pParamTypes = m_xParamTypes->getConstArray(); + + while (nParams--) + { + const typelib_MethodParameter & rParam = pTypelibParams[nParams]; + ParamInfo & rInfo = pParamInfos[nParams]; + rInfo.aName = rParam.pName; + if (rParam.bIn) + rInfo.aMode = (rParam.bOut ? ParamMode_INOUT : ParamMode_IN); + else + rInfo.aMode = ParamMode_OUT; + rInfo.aType = pParamTypes[nParams]; + } + } + else // make also param types sequence if not already initialized + { + Sequence< Reference< XIdlClass > > aTempParamTypes( nParams ); + Reference< XIdlClass > * pParamTypes = aTempParamTypes.getArray(); + + IdlReflectionServiceImpl * pRefl = getReflection(); + + while (nParams--) + { + const typelib_MethodParameter & rParam = pTypelibParams[nParams]; + ParamInfo & rInfo = pParamInfos[nParams]; + rInfo.aName = rParam.pName; + if (rParam.bIn) + rInfo.aMode = (rParam.bOut ? ParamMode_INOUT : ParamMode_IN); + else + rInfo.aMode = ParamMode_OUT; + rInfo.aType = pParamTypes[nParams] = pRefl->forType( rParam.pTypeRef ); + } + + m_xParamTypes = std::move(aTempParamTypes); + } + + m_xParamInfos = std::move(aTempParamInfos); + } + } + return *m_xParamInfos; +} + +MethodMode SAL_CALL IdlInterfaceMethodImpl::getMode() +{ + return + getMethodTypeDescr()->bOneWay ? MethodMode_ONEWAY : MethodMode_TWOWAY; +} + +Any SAL_CALL IdlInterfaceMethodImpl::invoke( const Any & rObj, Sequence< Any > & rArgs ) +{ + if (auto ifc = o3tl::tryAccess<css::uno::Reference<css::uno::XInterface>>( + rObj)) + { + // acquire()/ release() + if (rtl_ustr_ascii_compare( getTypeDescr()->pTypeName->buffer, + "com.sun.star.uno.XInterface::acquire" ) == 0) + { + (*ifc)->acquire(); + return Any(); + } + else if (rtl_ustr_ascii_compare( getTypeDescr()->pTypeName->buffer, + "com.sun.star.uno.XInterface::release" ) == 0) + { + (*ifc)->release(); + return Any(); + } + } + + uno_Interface * pUnoI = getReflection()->mapToUno( + rObj, reinterpret_cast<typelib_InterfaceTypeDescription *>(getDeclTypeDescr()) ); + OSL_ENSURE( pUnoI, "### illegal destination object given!" ); + if (pUnoI) + { + sal_Int32 nParams = getMethodTypeDescr()->nParams; + if (rArgs.getLength() != nParams) + { + (*pUnoI->release)( pUnoI ); + throw IllegalArgumentException( + "expected " + OUString::number(nParams) + + " arguments, got " + OUString::number(rArgs.getLength()), + *o3tl::doAccess<Reference<XInterface>>(rObj), 1 ); + } + + Any * pCppArgs = rArgs.getArray(); + typelib_MethodParameter * pParams = getMethodTypeDescr()->pParams; + typelib_TypeDescription * pReturnType = nullptr; + TYPELIB_DANGER_GET( + &pReturnType, getMethodTypeDescr()->pReturnTypeRef ); + + // C/C++ ABIs typically assume that structs are padded at the end, and + // that those padding bytes may be written to (e.g., to write into the + // end of a "short" struct by writing the full contents of a "long" + // register); so create enough space here (assuming that no ABI requires + // padding larger than 16 byte boundaries): + void * pUnoReturn = (pReturnType->nSize == 0) ? nullptr : alloca( multipleOf16(pReturnType->nSize) ); + void ** ppUnoArgs = static_cast<void **>(alloca( sizeof(void *) * nParams *2 )); + typelib_TypeDescription ** ppParamTypes = reinterpret_cast<typelib_TypeDescription **>(ppUnoArgs + nParams); + + // convert arguments + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + ppParamTypes[nPos] = nullptr; + TYPELIB_DANGER_GET( ppParamTypes + nPos, pParams[nPos].pTypeRef ); + typelib_TypeDescription * pTD = ppParamTypes[nPos]; + + ppUnoArgs[nPos] = alloca( pTD->nSize ); + if (pParams[nPos].bIn) + { + bool bAssign; + if (typelib_typedescriptionreference_equals( + pCppArgs[nPos].getValueTypeRef(), pTD->pWeakRef )) + { + uno_type_copyAndConvertData( + ppUnoArgs[nPos], const_cast<void *>(pCppArgs[nPos].getValue()), + pCppArgs[nPos].getValueTypeRef(), getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (pTD->eTypeClass == typelib_TypeClass_ANY) + { + uno_type_any_constructAndConvert( + static_cast<uno_Any *>(ppUnoArgs[nPos]), const_cast<void *>(pCppArgs[nPos].getValue()), + pCppArgs[nPos].getValueTypeRef(), getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (pTD->eTypeClass == typelib_TypeClass_INTERFACE) + { + Reference< XInterface > xDest; + bAssign = extract( + pCppArgs[nPos], reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD), + xDest, getReflection() ); + if (bAssign) + { + *static_cast<void **>(ppUnoArgs[nPos]) = getReflection()->getCpp2Uno().mapInterface( + xDest.get(), reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD) ); + } + } + else + { + typelib_TypeDescription * pValueTD = nullptr; + TYPELIB_DANGER_GET( &pValueTD, pCppArgs[nPos].getValueTypeRef() ); + // construct temp uno val to do proper assignment: todo opt + void * pTemp = alloca( pValueTD->nSize ); + uno_copyAndConvertData( + pTemp, const_cast<void *>(pCppArgs[nPos].getValue()), pValueTD, + getReflection()->getCpp2Uno().get() ); + uno_constructData( + ppUnoArgs[nPos], pTD ); + // assignment does simple conversion + bAssign = uno_assignData( + ppUnoArgs[nPos], pTD, pTemp, pValueTD, nullptr, nullptr, nullptr ); + uno_destructData( + pTemp, pValueTD, nullptr ); + TYPELIB_DANGER_RELEASE( pValueTD ); + } + + if (! bAssign) + { + IllegalArgumentException aExc( + "cannot coerce argument type during corereflection call:" + "\narg no.: " + OUString::number(nPos) + + " expected: \"" + OUString::unacquired(&pTD->pTypeName) + + "\" actual: \"" + OUString::unacquired(&pCppArgs[nPos].getValueTypeRef()->pTypeName) + + "\"", + *o3tl::doAccess<Reference<XInterface>>(rObj), static_cast<sal_Int16>(nPos) ); + + // cleanup + while (nPos--) + { + if (pParams[nPos].bIn) + uno_destructData( ppUnoArgs[nPos], ppParamTypes[nPos], nullptr ); + TYPELIB_DANGER_RELEASE( ppParamTypes[nPos] ); + } + TYPELIB_DANGER_RELEASE( pReturnType ); + (*pUnoI->release)( pUnoI ); + + throw aExc; + } + } + } + + uno_Any aUnoExc; + uno_Any * pUnoExc = &aUnoExc; + + (*pUnoI->pDispatcher)( + pUnoI, getTypeDescr(), pUnoReturn, ppUnoArgs, &pUnoExc ); + (*pUnoI->release)( pUnoI ); + + Any aRet; + if (pUnoExc) + { + // cleanup + while (nParams--) + { + if (pParams[nParams].bIn) + uno_destructData( ppUnoArgs[nParams], ppParamTypes[nParams], nullptr ); + TYPELIB_DANGER_RELEASE( ppParamTypes[nParams] ); + } + TYPELIB_DANGER_RELEASE( pReturnType ); + + uno_any_destruct(&aRet, + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_type_copyAndConvertData(&aRet, pUnoExc, cppu::UnoType<Any>::get().getTypeLibType(), + getReflection()->getUno2Cpp().get() ); + uno_any_destruct( pUnoExc, nullptr ); + throw InvocationTargetException("exception occurred during invocation!", + *o3tl::doAccess<Reference<XInterface>>(rObj), aRet); + } + else + { + // reconvert arguments and cleanup + while (nParams--) + { + if (pParams[nParams].bOut) // write back + { + uno_any_destruct( + &pCppArgs[nParams], + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_constructAndConvert( + &pCppArgs[nParams], ppUnoArgs[nParams], ppParamTypes[nParams], + getReflection()->getUno2Cpp().get() ); + } + uno_destructData( ppUnoArgs[nParams], ppParamTypes[nParams], nullptr ); + TYPELIB_DANGER_RELEASE( ppParamTypes[nParams] ); + } + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_constructAndConvert( + &aRet, pUnoReturn, pReturnType, + getReflection()->getUno2Cpp().get() ); + uno_destructData( pUnoReturn, pReturnType, nullptr ); + TYPELIB_DANGER_RELEASE( pReturnType ); + } + return aRet; + } + throw IllegalArgumentException( + "illegal destination object given!", + getXWeak(), 0 ); +} + + +InterfaceIdlClassImpl::~InterfaceIdlClassImpl() +{ + for ( sal_Int32 nPos = _nMethods + _nAttributes; nPos--; ) + typelib_typedescription_release( _pSortedMemberInit[nPos].second ); +} + + +Sequence< Reference< XIdlClass > > InterfaceIdlClassImpl::getSuperclasses() +{ + ::osl::MutexGuard aGuard(getMutexAccess()); + if (!_xSuperClasses.hasElements()) { + typelib_InterfaceTypeDescription * pType = getTypeDescr(); + _xSuperClasses.realloc(pType->nBaseTypes); + auto pSuperClasses = _xSuperClasses.getArray(); + for (sal_Int32 i = 0; i < pType->nBaseTypes; ++i) { + pSuperClasses[i] = getReflection()->forType( + &pType->ppBaseTypes[i]->aBase); + OSL_ASSERT(_xSuperClasses[i].is()); + } + } + return _xSuperClasses; +} + +void InterfaceIdlClassImpl::initMembers() +{ + sal_Int32 nAll = getTypeDescr()->nAllMembers; + std::unique_ptr<MemberInit[]> pSortedMemberInit(new MemberInit[nAll]); + typelib_TypeDescriptionReference ** ppAllMembers = getTypeDescr()->ppAllMembers; + + for ( sal_Int32 nPos = 0; nPos < nAll; ++nPos ) + { + sal_Int32 nIndex; + if (ppAllMembers[nPos]->eTypeClass == typelib_TypeClass_INTERFACE_METHOD) + { + // methods to front + nIndex = _nMethods; + ++_nMethods; + } + else + { + ++_nAttributes; + nIndex = (nAll - _nAttributes); + // attributes at the back + } + + typelib_TypeDescription * pTD = nullptr; + typelib_typedescriptionreference_getDescription( &pTD, ppAllMembers[nPos] ); + assert(pTD && "### cannot get type description!"); + pSortedMemberInit[nIndex].first = reinterpret_cast<typelib_InterfaceMemberTypeDescription *>(pTD)->pMemberName; + pSortedMemberInit[nIndex].second = pTD; + } + + _pSortedMemberInit = std::move(pSortedMemberInit); +} + +sal_Bool InterfaceIdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + if (xType.is() && xType->getTypeClass() == TypeClass_INTERFACE) + { + if (equals( xType )) + return true; + else + { + const Sequence< Reference< XIdlClass > > & rSeq = xType->getSuperclasses(); + if (std::any_of(rSeq.begin(), rSeq.end(), + [this](const Reference<XIdlClass>& rType){ return isAssignableFrom(rType); })) + return true; + } + } + return false; +} + +Uik InterfaceIdlClassImpl::getUik() +{ + return Uik(0, 0, 0, 0, 0); + // Uiks are deprecated and this function must not be called +} + +Sequence< Reference< XIdlMethod > > InterfaceIdlClassImpl::getMethods() +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + // create methods sequence + Sequence< Reference< XIdlMethod > > aRet( _nMethods ); + Reference< XIdlMethod > * pRet = aRet.getArray(); + for ( sal_Int32 nPos = _nMethods; nPos--; ) + { + + /*_aName2Method[_pSortedMemberInit[nPos].first] = */pRet[nPos] = new IdlInterfaceMethodImpl( + getReflection(), _pSortedMemberInit[nPos].first, + _pSortedMemberInit[nPos].second, IdlClassImpl::getTypeDescr() ); + } + return aRet; +} + +Sequence< Reference< XIdlField > > InterfaceIdlClassImpl::getFields() +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + // create fields sequence + Sequence< Reference< XIdlField > > aRet( _nAttributes ); + Reference< XIdlField > * pRet = aRet.getArray(); + for ( sal_Int32 nPos = _nAttributes; nPos--; ) + { + /*_aName2Field[_pSortedMemberInit[_nMethods+nPos].first] = */pRet[_nAttributes-nPos-1] = + new IdlAttributeFieldImpl( + getReflection(), _pSortedMemberInit[_nMethods+nPos].first, + _pSortedMemberInit[_nMethods+nPos].second, IdlClassImpl::getTypeDescr() ); + } + return aRet; +} + +Reference< XIdlMethod > InterfaceIdlClassImpl::getMethod( const OUString & rName ) +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + Reference< XIdlMethod > xRet; + + // try weak map + const OUString2Method::const_iterator iFind( _aName2Method.find( rName ) ); + if (iFind != _aName2Method.end()) + xRet = (*iFind).second; // harden ref + + if (! xRet.is()) + { + for ( sal_Int32 nPos = _nMethods; nPos--; ) + { + if (_pSortedMemberInit[nPos].first == rName) + { + _aName2Method[rName] = xRet = new IdlInterfaceMethodImpl( + getReflection(), rName, + _pSortedMemberInit[nPos].second, IdlClassImpl::getTypeDescr() ); + break; + } + } + } + return xRet; +} + +Reference< XIdlField > InterfaceIdlClassImpl::getField( const OUString & rName ) +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + Reference< XIdlField > xRet; + + // try weak map + const OUString2Field::const_iterator iFind( _aName2Field.find( rName ) ); + if (iFind != _aName2Field.end()) + xRet = (*iFind).second; // harden ref + + if (! xRet.is()) + { + for ( sal_Int32 nPos = _nAttributes; nPos--; ) + { + if (_pSortedMemberInit[_nMethods+nPos].first == rName) + { + _aName2Field[rName] = xRet = new IdlAttributeFieldImpl( + getReflection(), rName, + _pSortedMemberInit[_nMethods+nPos].second, IdlClassImpl::getTypeDescr() ); + break; + } + } + } + return xRet; +} + +void InterfaceIdlClassImpl::createObject( Any & rObj ) +{ + // interfaces cannot be constructed + rObj.clear(); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/dump.cxx b/stoc/source/corereflection/dump.cxx new file mode 100644 index 0000000000..0905393db9 --- /dev/null +++ b/stoc/source/corereflection/dump.cxx @@ -0,0 +1,358 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 <sal/config.h> + +#include <bitset> + +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/reflection/XConstantTypeDescription.hpp> +#include <com/sun/star/reflection/XConstantsTypeDescription.hpp> +#include <com/sun/star/reflection/XDump.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/DeploymentException.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Type.hxx> +#include <com/sun/star/uno/TypeClass.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <config_typesizes.h> +#include <cppu/unotype.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/interfacecontainer.hxx> +#include <o3tl/unreachable.hxx> +#include <osl/mutex.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <typelib/typedescription.h> +#include <typelib/typedescription.hxx> +#include <uno/sequence2.h> + +namespace com::sun::star::uno +{ +class XInterface; +} + +namespace +{ +template <typename T> OUString hex(T value, sal_Int32 width) +{ + OUStringBuffer buf(OUString::number(value, 16)); + while (buf.getLength() < width) + { + buf.insert(0, '0'); + } + return buf.makeStringAndClear(); +} + +css::uno::TypeDescription getTypeDescription(css::uno::Type const& type) +{ + typelib_TypeDescription* d = nullptr; + type.getDescription(&d); + return css::uno::TypeDescription(d); +} + +OUString +getIdentifier(css::uno::Reference<css::reflection::XConstantTypeDescription> const& constant) +{ + auto const n = constant->getName(); + auto const i = n.lastIndexOf('.'); + if (i == -1 || i == n.getLength() - 1) + { + throw css::uno::DeploymentException("bad constant name " + n); + } + return n.copy(i + 1); +} + +OUString +dumpBitset(css::uno::Sequence<css::uno::Reference<css::reflection::XConstantTypeDescription>> const& + constants, + sal_uInt64 value) +{ + OUStringBuffer buf; + auto a = value; + for (auto const& i : constants) + { + sal_uInt64 c; + if ((i->getConstantValue() >>= c) && std::bitset<64>{ c }.count() == 1 && (a & c) != 0) + { + if (!buf.isEmpty()) + { + buf.append('+'); + } + buf.append(getIdentifier(i)); + a &= ~c; + } + } + return a == 0 && !buf.isEmpty() ? buf.makeStringAndClear() : OUString::number(value); +} + +class Dump : public cppu::BaseMutex, public cppu::WeakComponentImplHelper<css::reflection::XDump> +{ +public: + explicit Dump(css::uno::Reference<css::uno::XComponentContext> const& context) + : WeakComponentImplHelper(m_aMutex) + , manager_(context->getValueByName( + "/singletons/com.sun.star.reflection.theTypeDescriptionManager"), + css::uno::UNO_QUERY_THROW) + { + } + + void SAL_CALL disposing() override + { + osl::MutexGuard g(m_aMutex); + manager_.clear(); + } + + OUString SAL_CALL dumpValue(css::uno::Any const& value) override + { + switch (value.getValueTypeClass()) + { + case css::uno::TypeClass_VOID: + return "void"; + case css::uno::TypeClass_BOOLEAN: + return OUString::boolean(value.get<bool>()); + case css::uno::TypeClass_BYTE: + return OUString::number(value.get<sal_Int8>()); + case css::uno::TypeClass_SHORT: + return OUString::number(value.get<sal_Int16>()); + case css::uno::TypeClass_UNSIGNED_SHORT: + return OUString::number(value.get<sal_uInt16>()); + case css::uno::TypeClass_LONG: + return OUString::number(value.get<sal_Int32>()); + case css::uno::TypeClass_UNSIGNED_LONG: + return OUString::number(value.get<sal_uInt32>()); + case css::uno::TypeClass_HYPER: + return OUString::number(value.get<sal_Int64>()); + case css::uno::TypeClass_UNSIGNED_HYPER: + return OUString::number(value.get<sal_uInt64>()); + case css::uno::TypeClass_FLOAT: + return OUString::number(value.get<float>()); + case css::uno::TypeClass_DOUBLE: + return OUString::number(value.get<double>()); + case css::uno::TypeClass_CHAR: + return "U+" + hex(value.get<sal_Unicode>(), 16); + case css::uno::TypeClass_STRING: + { + auto const s = value.get<OUString>(); + OUStringBuffer buf; + for (sal_Int32 i = 0; i != s.getLength();) + { + auto const c = s.iterateCodePoints(&i); + if (c >= u8' ' && c <= u8'~') + { + if (c == u8'\"' || c == u8'\\') + { + buf.append('\\'); + } + buf.append(char(c)); + } + else if (c <= 0xFFFF) + { + buf.append("\\u" + hex(c, 4)); + } + else + { + buf.append("\\U" + hex(c, 8)); + } + } + return "\"" + buf + "\""; + } + case css::uno::TypeClass_TYPE: + return value.get<css::uno::Type>().getTypeName(); + case css::uno::TypeClass_SEQUENCE: + { + css::uno::Type const t(reinterpret_cast<typelib_IndirectTypeDescription const*>( + getTypeDescription(value.getValueType()).get()) + ->pType); + auto const n = getTypeDescription(t).get()->nSize; + auto const s = *static_cast<uno_Sequence* const*>(value.getValue()); + OUStringBuffer buf; + for (sal_Int32 i = 0; i != s->nElements; ++i) + { + if (i != 0) + { + buf.append(", "); + } + css::uno::Any const e(s->elements + i * n, t); + buf.append(t == cppu::UnoType<css::uno::Any>::get() ? dumpAny(e) + : dumpValue(e)); + } + return "[" + buf + "]"; + } + case css::uno::TypeClass_ENUM: + { + auto const d = getTypeDescription(value.getValueType()); + auto const ed = reinterpret_cast<typelib_EnumTypeDescription const*>(d.get()); + auto const e = *static_cast<sal_Int32 const*>(value.getValue()); + for (sal_Int32 i = 0; i != ed->nEnumValues; ++i) + { + if (ed->pEnumValues[i] == e) + { + return OUString(ed->ppEnumNames[i]); + } + } + return OUString::number(e); + } + case css::uno::TypeClass_STRUCT: + case css::uno::TypeClass_EXCEPTION: + { + auto const d = getTypeDescription(value.getValueType()); + OUStringBuffer buf; + dumpCompoundType(reinterpret_cast<typelib_CompoundTypeDescription const*>(d.get()), + value.getValue(), &buf); + return "[" + buf + "]"; + } + case css::uno::TypeClass_INTERFACE: + { + auto const p = *static_cast<void* const*>(value.getValue()); + return p == nullptr ? OUString("null") + : OUString("0x" + + hex(reinterpret_cast<sal_uIntPtr>(p), + SAL_TYPES_SIZEOFPOINTER * 2)); + } + default: + O3TL_UNREACHABLE; + } + } + + OUString SAL_CALL dumpAny(css::uno::Any const& value) override + { + return "[" + value.getValueType().getTypeName() + ": " + dumpValue(value) + "]"; + } + + OUString SAL_CALL dumpConstant(OUString const& constantsGroup, + css::uno::Any const& value) override + { + css::uno::Reference<css::container::XHierarchicalNameAccess> manager; + { + osl::MutexGuard g(m_aMutex); + if (rBHelper.bDisposed) + { + throw css::lang::DisposedException("css.reflection.Dumper"); + } + manager = manager_; + } + css::uno::Reference<css::reflection::XConstantsTypeDescription> g; + try + { + manager_->getByHierarchicalName(constantsGroup) >>= g; + } + catch (css::container::NoSuchElementException) + { + } + if (!g.is()) + { + throw css::lang::IllegalArgumentException("not a constants group: " + constantsGroup, + {}, 0); + } + auto const s = g->getConstants(); + switch (value.getValueTypeClass()) + { + case css::uno::TypeClass_BOOLEAN: + for (auto const& i : s) + { + if (i->getConstantValue() == value) + { + return getIdentifier(i); + } + } + return OUString::boolean(value.get<bool>()); + case css::uno::TypeClass_BYTE: + case css::uno::TypeClass_SHORT: + case css::uno::TypeClass_LONG: + case css::uno::TypeClass_HYPER: + { + auto const v = value.get<sal_Int64>(); + for (auto const& i : s) + { + sal_Int64 c; + if ((i->getConstantValue() >>= c) && c == v) + { + return getIdentifier(i); + } + } + return v >= 0 ? dumpBitset(s, v) : OUString::number(v); + } + case css::uno::TypeClass_UNSIGNED_SHORT: + case css::uno::TypeClass_UNSIGNED_LONG: + case css::uno::TypeClass_UNSIGNED_HYPER: + { + auto const v = value.get<sal_uInt64>(); + for (auto const& i : s) + { + sal_uInt64 c; + if ((i->getConstantValue() >>= c) && c == v) + { + return getIdentifier(i); + } + } + return dumpBitset(s, v); + } + case css::uno::TypeClass_FLOAT: + case css::uno::TypeClass_DOUBLE: + { + auto const v = value.get<double>(); + for (auto const& i : s) + { + double c; + if ((i->getConstantValue() >>= c) && c == v) + { + return getIdentifier(i); + } + } + return OUString::number(v); + } + default: + throw css::lang::IllegalArgumentException( + "not a numeric type: " + value.getValueTypeName(), {}, 1); + } + } + +private: + css::uno::Reference<css::container::XHierarchicalNameAccess> manager_; + + void dumpCompoundType(typelib_CompoundTypeDescription const* description, void const* data, + OUStringBuffer* buffer) + { + if (auto base = description->pBaseTypeDescription) + { + dumpCompoundType(base, data, buffer); + } + for (sal_Int32 i = 0; i != description->nMembers; ++i) + { + if (!buffer->isEmpty()) + { + buffer->append(", "); + } + buffer->append(OUString::unacquired(description->ppMemberNames + i) + ": "); + css::uno::Type t(description->ppTypeRefs[i]); + css::uno::Any const m(static_cast<char const*>(data) + description->pMemberOffsets[i], + t); + buffer->append(t == cppu::UnoType<css::uno::Any>::get() ? dumpAny(m) : dumpValue(m)); + } + } +}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_stoc_Dump_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence<css::uno::Any> const& arguments) +{ + SAL_WARN_IF(arguments.hasElements(), "stoc", "unexpected singleton arguments"); + return cppu::acquire(new Dump(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/stoc/source/corereflection/lrucache.hxx b/stoc/source/corereflection/lrucache.hxx new file mode 100644 index 0000000000..d5eebe804f --- /dev/null +++ b/stoc/source/corereflection/lrucache.hxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STOC_SOURCE_COREREFLECTION_LRUCACHE_HXX +#define INCLUDED_STOC_SOURCE_COREREFLECTION_LRUCACHE_HXX + +// __CACHE_DIAGNOSE forces cache size to 4 and works only for OUString keys +// #define __CACHE_DIAGNOSE 1 + +#include <rtl/ustring.hxx> + +#include <memory> +#include <mutex> +#include <unordered_map> + +namespace com::sun::star::uno { class Any; } + +/** Implementation of a least recently used (lru) cache. + <br> +*/ +template< class t_Key, class t_Val, class t_KeyHash > +class LRU_Cache +{ + struct CacheEntry + { + t_Key aKey; + t_Val aVal; + CacheEntry * pPred; + CacheEntry * pSucc; + }; + typedef std::unordered_map< t_Key, CacheEntry *, t_KeyHash > t_Key2Element; + + mutable std::mutex _aCacheMutex; + sal_Int32 _nCachedElements; + t_Key2Element _aKey2Element; + + std::unique_ptr<CacheEntry[]> _pBlock; + mutable CacheEntry * _pHead; + mutable CacheEntry * _pTail; + inline void toFront( CacheEntry * pEntry ) const; + +public: + /** Constructor: + <br> + @param nCachedElements number of elements to be cached; default param set to 128 + */ + explicit inline LRU_Cache(); + + /** Retrieves a value from the cache. Returns default constructed value, + if none was found. + <br> + @param rKey a key + @return value + */ + inline t_Val getValue( const t_Key & rKey ) const; + /** Sets a value to be cached for given key. + <br> + @param rKey a key + @param rValue a value + */ + inline void setValue( const t_Key & rKey, const t_Val & rValue ); + /** Clears the cache, thus releasing all cached elements and keys. + <br> + */ + inline void clear(); +}; + +template< class t_Key, class t_Val, class t_KeyHash > +inline LRU_Cache< t_Key, t_Val, t_KeyHash >::LRU_Cache() +#ifdef __CACHE_DIAGNOSE + : _nCachedElements( 4 ) +#else + : _nCachedElements( 256 ) +#endif + , _pBlock( nullptr ) + , _pHead( nullptr ) + , _pTail( nullptr ) +{ + _pBlock.reset(new CacheEntry[_nCachedElements]); + _pHead = _pBlock.get(); + _pTail = _pBlock.get() + _nCachedElements - 1; + for (sal_Int32 nPos = _nCachedElements; nPos--;) + { + _pBlock[nPos].pPred = _pBlock.get() + nPos - 1; + _pBlock[nPos].pSucc = _pBlock.get() + nPos + 1; + } +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline void LRU_Cache< t_Key, t_Val, t_KeyHash >::toFront( CacheEntry * pEntry ) const +{ + if (pEntry != _pHead) + { + // cut out element + if (pEntry == _pTail) + { + _pTail = pEntry->pPred; + } + else + { + pEntry->pSucc->pPred = pEntry->pPred; + pEntry->pPred->pSucc = pEntry->pSucc; + } + // push to front + _pHead->pPred = pEntry; + pEntry->pSucc = _pHead; + _pHead = pEntry; + } +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline t_Val LRU_Cache< t_Key, t_Val, t_KeyHash >::getValue( const t_Key & rKey ) const +{ + std::scoped_lock aGuard( _aCacheMutex ); + const typename t_Key2Element::const_iterator iFind( _aKey2Element.find( rKey ) ); + if (iFind != _aKey2Element.end()) + { + CacheEntry * pEntry = (*iFind).second; + toFront( pEntry ); +#ifdef __CACHE_DIAGNOSE + SAL_INFO("stoc.corerefl", "> retrieved element \"" ); + SAL_INFO("stoc.corerefl", "" << pEntry->aKey); + SAL_INFO("stoc.corerefl", "\" from cache <" ); +#endif + return pEntry->aVal; + } + return t_Val(); +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline void LRU_Cache< t_Key, t_Val, t_KeyHash >::setValue( + const t_Key & rKey, const t_Val & rValue ) +{ + std::scoped_lock aGuard( _aCacheMutex ); + if (_nCachedElements > 0) + { + const typename t_Key2Element::const_iterator iFind( _aKey2Element.find( rKey ) ); + + CacheEntry * pEntry; + if (iFind == _aKey2Element.end()) + { + pEntry = _pTail; // erase last element +#ifdef __CACHE_DIAGNOSE + if (pEntry->aKey.getLength()) + { + SAL_INFO("stoc.corerefl", "> kicking element \"" ); + SAL_INFO("stoc.corerefl", "" << pEntry->aKey); + SAL_INFO("stoc.corerefl", "\" from cache <" ); + } +#endif + _aKey2Element.erase( pEntry->aKey ); + pEntry->aKey = rKey; + _aKey2Element[ rKey ] = pEntry; + } + else + { + pEntry = (*iFind).second; +#ifdef __CACHE_DIAGNOSE + SAL_INFO("stoc.corerefl", "> replacing element \"" ); + SAL_INFO("stoc.corerefl", "" << pEntry->aKey); + SAL_INFO("stoc.corerefl", "\" in cache <" ); +#endif + } + pEntry->aVal = rValue; + toFront( pEntry ); + } +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline void LRU_Cache< t_Key, t_Val, t_KeyHash >::clear() +{ + std::scoped_lock aGuard( _aCacheMutex ); + _aKey2Element.clear(); + for ( sal_Int32 nPos = _nCachedElements; nPos--; ) + { + _pBlock[nPos].aKey = t_Key(); + _pBlock[nPos].aVal = t_Val(); + } + _nCachedElements = 0; +#ifdef __CACHE_DIAGNOSE + SAL_INFO("stoc.corerefl", "> cleared cache <" ); +#endif +} + + +/** Template instance for OUString keys, Any values.<br> +*/ +typedef LRU_Cache< OUString, css::uno::Any, OUStringHash > + LRU_CacheAnyByOUString; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/reflection.component b/stoc/source/corereflection/reflection.component new file mode 100644 index 0000000000..0210c80904 --- /dev/null +++ b/stoc/source/corereflection/reflection.component @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.CoreReflection" + constructor="com_sun_star_comp_stoc_CoreReflection_get_implementation" + single-instance="true"> + <service name="com.sun.star.reflection.CoreReflection"/> + <singleton name="com.sun.star.reflection.theCoreReflection"/> + </implementation> + <implementation name="com.sun.star.comp.stoc.Dump" + constructor="com_sun_star_comp_stoc_Dump_get_implementation" + single-instance="true"> + <singleton name="com.sun.star.reflection.Dump"/> + </implementation> +</component> diff --git a/stoc/source/defaultregistry/defaultregistry.cxx b/stoc/source/defaultregistry/defaultregistry.cxx new file mode 100644 index 0000000000..815d7a6679 --- /dev/null +++ b/stoc/source/defaultregistry/defaultregistry.cxx @@ -0,0 +1,1186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/mutex.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ref.hxx> + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace css::uno; +using namespace css::registry; +using namespace css::lang; +using namespace css::container; +using namespace cppu; +using namespace osl; + +namespace { + +class NestedRegistryImpl : public WeakImplHelper < XSimpleRegistry, XInitialization, XServiceInfo, XEnumerationAccess > +{ +public: + NestedRegistryImpl( ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // XSimpleRegistry + virtual OUString SAL_CALL getURL() override; + virtual void SAL_CALL open( const OUString& rURL, sal_Bool bReadOnly, sal_Bool bCreate ) override; + virtual sal_Bool SAL_CALL isValid( ) override; + virtual void SAL_CALL close( ) override; + virtual void SAL_CALL destroy( ) override; + virtual Reference< XRegistryKey > SAL_CALL getRootKey( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL mergeKey( const OUString& aKeyName, const OUString& aUrl ) override; + + // XEnumerationAccess + virtual Reference< XEnumeration > SAL_CALL createEnumeration( ) override; + virtual Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + friend class NestedKeyImpl; +protected: + Mutex m_mutex; + sal_uInt32 m_state; + Reference<XSimpleRegistry> m_localReg; + Reference<XSimpleRegistry> m_defaultReg; + +}; + + +// class NestedKeyImpl the implementation of interface XRegistryKey + +class NestedKeyImpl : public WeakImplHelper< XRegistryKey > +{ +public: + NestedKeyImpl( NestedRegistryImpl* pDefaultRegistry, + Reference<XRegistryKey>& localKey, + Reference<XRegistryKey>& defaultKey); + + NestedKeyImpl( const OUString& aKeyName, + NestedKeyImpl* pKey); + + // XRegistryKey + virtual OUString SAL_CALL getKeyName() override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL isValid( ) override; + virtual RegistryKeyType SAL_CALL getKeyType( const OUString& rKeyName ) override; + virtual RegistryValueType SAL_CALL getValueType( ) override; + virtual sal_Int32 SAL_CALL getLongValue( ) override; + virtual void SAL_CALL setLongValue( sal_Int32 value ) override; + virtual Sequence< sal_Int32 > SAL_CALL getLongListValue( ) override; + virtual void SAL_CALL setLongListValue( const css::uno::Sequence< sal_Int32 >& seqValue ) override; + virtual OUString SAL_CALL getAsciiValue( ) override; + virtual void SAL_CALL setAsciiValue( const OUString& value ) override; + virtual Sequence< OUString > SAL_CALL getAsciiListValue( ) override; + virtual void SAL_CALL setAsciiListValue( const css::uno::Sequence< OUString >& seqValue ) override; + virtual OUString SAL_CALL getStringValue( ) override; + virtual void SAL_CALL setStringValue( const OUString& value ) override; + virtual Sequence< OUString > SAL_CALL getStringListValue( ) override; + virtual void SAL_CALL setStringListValue( const css::uno::Sequence< OUString >& seqValue ) override; + virtual Sequence< sal_Int8 > SAL_CALL getBinaryValue( ) override; + virtual void SAL_CALL setBinaryValue( const css::uno::Sequence< sal_Int8 >& value ) override; + virtual Reference< XRegistryKey > SAL_CALL openKey( const OUString& aKeyName ) override; + virtual Reference< XRegistryKey > SAL_CALL createKey( const OUString& aKeyName ) override; + virtual void SAL_CALL closeKey( ) override; + virtual void SAL_CALL deleteKey( const OUString& rKeyName ) override; + virtual Sequence< Reference< XRegistryKey > > SAL_CALL openKeys( ) override; + virtual Sequence< OUString > SAL_CALL getKeyNames( ) override; + virtual sal_Bool SAL_CALL createLink( const OUString& aLinkName, const OUString& aLinkTarget ) override; + virtual void SAL_CALL deleteLink( const OUString& rLinkName ) override; + virtual OUString SAL_CALL getLinkTarget( const OUString& rLinkName ) override; + virtual OUString SAL_CALL getResolvedName( const OUString& aKeyName ) override; + +protected: + void computeChanges(); + OUString computeName(const OUString& name); + + OUString m_name; + sal_uInt32 m_state; + rtl::Reference<NestedRegistryImpl> m_xRegistry; + Reference<XRegistryKey> m_localKey; + Reference<XRegistryKey> m_defaultKey; +}; + + +NestedKeyImpl::NestedKeyImpl( NestedRegistryImpl* pDefaultRegistry, + Reference<XRegistryKey>& localKey, + Reference<XRegistryKey>& defaultKey ) + : m_state(pDefaultRegistry->m_state), m_xRegistry(pDefaultRegistry), m_localKey(localKey), m_defaultKey(defaultKey) +{ + if (m_localKey.is()) + { + m_name = m_localKey->getKeyName(); + } + else if (m_defaultKey.is()) + { + m_name = m_defaultKey->getKeyName(); + } +} + + +NestedKeyImpl::NestedKeyImpl( const OUString& rKeyName, + NestedKeyImpl* pKey) + : m_xRegistry(pKey->m_xRegistry) +{ + if (pKey->m_localKey.is() && pKey->m_localKey->isValid()) + { + m_localKey = pKey->m_localKey->openKey(rKeyName); + } + if (pKey->m_defaultKey.is() && pKey->m_defaultKey->isValid()) + { + m_defaultKey = pKey->m_defaultKey->openKey(rKeyName); + } + + if (m_localKey.is()) + { + m_name = m_localKey->getKeyName(); + } + else if (m_defaultKey.is()) + { + m_name = m_defaultKey->getKeyName(); + } + + m_state = m_xRegistry->m_state; +} + +void NestedKeyImpl::computeChanges() +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( m_state == m_xRegistry->m_state ) + return; + + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + + Reference<XRegistryKey> tmpKey = rootKey->openKey(m_name); + + if ( tmpKey.is() ) + { + m_localKey = rootKey->openKey(m_name); + } + + m_state = m_xRegistry->m_state; +} + + +// NestedKey_Impl::computeName() + +OUString NestedKeyImpl::computeName(const OUString& name) +{ + OUString resLocalName, resDefaultName; + + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + try + { + if ( m_localKey.is() && m_localKey->isValid() ) + { + resLocalName = m_localKey->getResolvedName(name); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getResolvedName(name); + } + + if ( !resLocalName.isEmpty() && m_xRegistry->m_defaultReg->isValid() ) + { + Reference<XRegistryKey> localRoot(m_xRegistry->m_localReg->getRootKey()); + Reference<XRegistryKey> defaultRoot(m_xRegistry->m_defaultReg->getRootKey()); + + resDefaultName = defaultRoot->getResolvedName(resLocalName); + + sal_uInt32 count = 100; + + while (resLocalName != resDefaultName && count > 0) + { + count--; + + if (resLocalName.isEmpty() || resDefaultName.isEmpty()) + throw InvalidRegistryException(); + + resLocalName = localRoot->getResolvedName(resDefaultName); + resDefaultName = defaultRoot->getResolvedName(resLocalName); + } + } + } + catch(InvalidRegistryException& ) + { + } + + return resLocalName; +} + + +OUString SAL_CALL NestedKeyImpl::getKeyName() +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + return m_name; +} + + +sal_Bool SAL_CALL NestedKeyImpl::isReadOnly( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( !m_localKey.is() || !m_localKey->isValid() ) + throw InvalidRegistryException(); + + return m_localKey->isReadOnly(); +} + + +sal_Bool SAL_CALL NestedKeyImpl::isValid( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + return ((m_localKey.is() && m_localKey->isValid()) || + (m_defaultKey.is() && m_defaultKey->isValid()) ); +} + + +RegistryKeyType SAL_CALL NestedKeyImpl::getKeyType( const OUString& rKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getKeyType(rKeyName); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getKeyType(rKeyName); + } + + return RegistryKeyType_KEY; +} + + +RegistryValueType SAL_CALL NestedKeyImpl::getValueType( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getValueType(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getValueType(); + } + + return RegistryValueType_NOT_DEFINED; +} + + +sal_Int32 SAL_CALL NestedKeyImpl::getLongValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getLongValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getLongValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setLongValue( sal_Int32 value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setLongValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setLongValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< sal_Int32 > SAL_CALL NestedKeyImpl::getLongListValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getLongListValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getLongListValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setLongListValue( const Sequence< sal_Int32 >& seqValue ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setLongListValue(seqValue); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setLongListValue(seqValue); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +OUString SAL_CALL NestedKeyImpl::getAsciiValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getAsciiValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getAsciiValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setAsciiValue( const OUString& value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setAsciiValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setAsciiValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< OUString > SAL_CALL NestedKeyImpl::getAsciiListValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getAsciiListValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getAsciiListValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setAsciiListValue( const Sequence< OUString >& seqValue ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setAsciiListValue(seqValue); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setAsciiListValue(seqValue); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +OUString SAL_CALL NestedKeyImpl::getStringValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getStringValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getStringValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setStringValue( const OUString& value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setStringValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setStringValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< OUString > SAL_CALL NestedKeyImpl::getStringListValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getStringListValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getStringListValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setStringListValue( const Sequence< OUString >& seqValue ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setStringListValue(seqValue); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setStringListValue(seqValue); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< sal_Int8 > SAL_CALL NestedKeyImpl::getBinaryValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getBinaryValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getBinaryValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setBinaryValue( const Sequence< sal_Int8 >& value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setBinaryValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setBinaryValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Reference< XRegistryKey > SAL_CALL NestedKeyImpl::openKey( const OUString& aKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(aKeyName); + + if ( resolvedName.isEmpty() ) + throw InvalidRegistryException(); + + Reference<XRegistryKey> localKey, defaultKey; + + if ( m_localKey.is() && m_localKey->isValid() ) + { + localKey = m_xRegistry->m_localReg->getRootKey()->openKey(resolvedName); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultKey = m_xRegistry->m_defaultReg->getRootKey()->openKey(resolvedName); + } + + if ( localKey.is() || defaultKey.is() ) + { + return new NestedKeyImpl(m_xRegistry.get(), localKey, defaultKey); + } + else + { + return Reference<XRegistryKey>(); + } +} + + +Reference< XRegistryKey > SAL_CALL NestedKeyImpl::createKey( const OUString& aKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( (!m_localKey.is() && !m_defaultKey.is()) || + (m_localKey.is() && m_localKey->isReadOnly()) ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(aKeyName); + + if ( resolvedName.isEmpty() ) + throw InvalidRegistryException(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + Reference<XRegistryKey> localKey, defaultKey; + + localKey = m_xRegistry->m_localReg->getRootKey()->createKey(resolvedName); + if ( localKey.is() ) + { + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultKey = m_xRegistry->m_defaultReg->getRootKey()->openKey(resolvedName); + } + + m_state = m_xRegistry->m_state++; + + return new NestedKeyImpl(m_xRegistry.get(), localKey, defaultKey); + } + } + else + { + Reference<XRegistryKey> localKey, defaultKey; + + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + + localKey = m_xRegistry->m_localReg->getRootKey()->createKey(resolvedName); + + if ( localKey.is() ) + { + defaultKey = m_xRegistry->m_defaultReg->getRootKey()->openKey(resolvedName); + + m_state = m_xRegistry->m_state++; + + return new NestedKeyImpl(m_xRegistry.get(), localKey, defaultKey); + } + } + } + + return Reference<XRegistryKey>(); +} + + +void SAL_CALL NestedKeyImpl::closeKey( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->closeKey(); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + m_defaultKey->closeKey(); + } +} + + +void SAL_CALL NestedKeyImpl::deleteKey( const OUString& rKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() || !m_localKey->isValid() || + m_localKey->isReadOnly() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(rKeyName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + m_xRegistry->m_localReg->getRootKey()->deleteKey(resolvedName); +} + + +Sequence< Reference< XRegistryKey > > SAL_CALL NestedKeyImpl::openKeys( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + Sequence<OUString> localSeq, defaultSeq; + + if ( m_localKey.is() && m_localKey->isValid() ) + { + localSeq = m_localKey->getKeyNames(); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultSeq = m_defaultKey->getKeyNames(); + } + + std::vector< Reference<XRegistryKey> > retVec; + retVec.reserve(localSeq.getLength() + defaultSeq.getLength()); + + auto lKeyNameToRegKey = [this](const OUString& rName) -> Reference<XRegistryKey> { + sal_Int32 lastIndex = rName.lastIndexOf('/'); + OUString name = rName.copy(lastIndex); + return new NestedKeyImpl(name, this); + }; + + for (const auto& rKeyName : std::as_const(localSeq)) + retVec.push_back(lKeyNameToRegKey(rKeyName)); + + for (const auto& rKeyName : std::as_const(defaultSeq)) + { + if ( comphelper::findValue(localSeq, rKeyName) == -1 ) + { + retVec.push_back(lKeyNameToRegKey(rKeyName)); + } + } + + return comphelper::containerToSequence(retVec); +} + + +Sequence< OUString > SAL_CALL NestedKeyImpl::getKeyNames( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + Sequence<OUString> localSeq, defaultSeq; + + if ( m_localKey.is() && m_localKey->isValid() ) + { + localSeq = m_localKey->getKeyNames(); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultSeq = m_defaultKey->getKeyNames(); + } + + return comphelper::combineSequences(localSeq, defaultSeq); +} + + +sal_Bool SAL_CALL NestedKeyImpl::createLink( const OUString& aLinkName, const OUString& aLinkTarget ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + + bool isCreated = false; + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName; + sal_Int32 lastIndex = aLinkName.lastIndexOf('/'); + + if ( lastIndex > 0 ) + { + OUString linkName = aLinkName.copy(0, lastIndex); + + resolvedName = computeName(linkName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + resolvedName += aLinkName.subView(lastIndex); + } + else + { + if ( lastIndex == 0 ) + resolvedName = m_name + aLinkName; + else + resolvedName = m_name + "/" + aLinkName; + } + + if ( m_localKey.is() && m_localKey->isValid() ) + { + isCreated = m_xRegistry->m_localReg->getRootKey()->createLink(resolvedName, aLinkTarget); + } + else + { + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + + isCreated = m_xRegistry->m_localReg->getRootKey()->createLink(resolvedName, aLinkTarget); + } + } + + if ( isCreated ) + m_state = m_xRegistry->m_state++; + + return isCreated; +} + + +void SAL_CALL NestedKeyImpl::deleteLink( const OUString& rLinkName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName; + sal_Int32 lastIndex = rLinkName.lastIndexOf('/'); + + if ( lastIndex > 0 ) + { + OUString linkName = rLinkName.copy(0, lastIndex); + + resolvedName = computeName(linkName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + resolvedName += rLinkName.subView(lastIndex); + } + else + { + if ( lastIndex == 0 ) + resolvedName = m_name + rLinkName; + else + resolvedName = m_name + "/" + rLinkName; + } + + if ( !m_localKey.is() || !m_localKey->isValid() || + m_localKey->isReadOnly() ) + { + throw InvalidRegistryException(); + } + + m_xRegistry->m_localReg->getRootKey()->deleteLink(resolvedName); +} + + +OUString SAL_CALL NestedKeyImpl::getLinkTarget( const OUString& rLinkName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName; + sal_Int32 lastIndex = rLinkName.lastIndexOf('/'); + + if ( lastIndex > 0 ) + { + OUString linkName = rLinkName.copy(0, lastIndex); + + resolvedName = computeName(linkName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + resolvedName += rLinkName.subView(lastIndex); + } + else + { + if ( lastIndex == 0 ) + resolvedName = m_name + rLinkName; + else + resolvedName = m_name + "/" + rLinkName; + } + + OUString linkTarget; + if ( m_localKey.is() && m_localKey->isValid() ) + { + try + { + linkTarget = m_xRegistry->m_localReg->getRootKey()->getLinkTarget(resolvedName); + return linkTarget; + } + catch(InvalidRegistryException& ) + { + } + } + + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + linkTarget = m_xRegistry->m_defaultReg->getRootKey()->getLinkTarget(resolvedName); + + return linkTarget; +} + + +OUString SAL_CALL NestedKeyImpl::getResolvedName( const OUString& aKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(aKeyName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + return resolvedName; +} + + +// DefaultRegistry Implementation + + +NestedRegistryImpl::NestedRegistryImpl( ) + : m_state(0) +{} + +class RegistryEnumueration : public WeakImplHelper< XEnumeration > +{ +public: + RegistryEnumueration( + const Reference< XSimpleRegistry > &r1, + const Reference< XSimpleRegistry > &r2 ) + : m_xReg1( r1 ) , m_xReg2( r2 ) + {} +public: + virtual sal_Bool SAL_CALL hasMoreElements( ) override; + virtual Any SAL_CALL nextElement( ) override; + +private: + Reference< XSimpleRegistry > m_xReg1; + Reference< XSimpleRegistry > m_xReg2; +}; + +sal_Bool RegistryEnumueration::hasMoreElements( ) +{ + return m_xReg1.is() || m_xReg2.is(); +} + +Any RegistryEnumueration::nextElement( ) +{ + Any a; + if( m_xReg1.is() ) + { + a <<= m_xReg1; + m_xReg1.clear(); + } + else if( m_xReg2.is() ) + { + a <<= m_xReg2; + m_xReg2.clear(); + } + else + { + throw NoSuchElementException( "NestedRegistry: no nextElement() !" ); + } + return a; +} + + +Reference< XEnumeration > NestedRegistryImpl::createEnumeration( ) +{ + MutexGuard guard( m_mutex ); + return new RegistryEnumueration( m_localReg, m_defaultReg ); +} + +Type NestedRegistryImpl::getElementType( ) +{ + return cppu::UnoType<decltype(m_localReg)>::get(); +} + +sal_Bool SAL_CALL NestedRegistryImpl::hasElements( ) +{ + MutexGuard guard( m_mutex ); + return m_localReg.is() || m_defaultReg.is(); +} + + +OUString SAL_CALL NestedRegistryImpl::getImplementationName( ) +{ + return "com.sun.star.comp.stoc.NestedRegistry"; +} + +sal_Bool SAL_CALL NestedRegistryImpl::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL NestedRegistryImpl::getSupportedServiceNames( ) +{ + Sequence< OUString > seqNames { "com.sun.star.registry.NestedRegistry" }; + return seqNames; +} + + +void SAL_CALL NestedRegistryImpl::initialize( const Sequence< Any >& aArguments ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( (aArguments.getLength() == 2) && + (aArguments[0].getValueType().getTypeClass() == TypeClass_INTERFACE) && + (aArguments[1].getValueType().getTypeClass() == TypeClass_INTERFACE) ) + { + aArguments[0] >>= m_localReg; + aArguments[1] >>= m_defaultReg; + if ( m_localReg == m_defaultReg ) + m_defaultReg.clear(); + } +} + + +OUString SAL_CALL NestedRegistryImpl::getURL() +{ + Guard< Mutex > aGuard( m_mutex ); + try + { + if ( m_localReg.is() && m_localReg->isValid() ) + return m_localReg->getURL(); + } + catch(InvalidRegistryException& ) + { + } + + return OUString(); +} + + +void SAL_CALL NestedRegistryImpl::open( const OUString&, sal_Bool, sal_Bool ) +{ + throw InvalidRegistryException( + "the 'open' method is not specified for a nested registry" ); +} + + +sal_Bool SAL_CALL NestedRegistryImpl::isValid( ) +{ + Guard< Mutex > aGuard( m_mutex ); + try + { + if ( (m_localReg.is() && m_localReg->isValid()) || + (m_defaultReg.is() && m_defaultReg->isValid()) ) + return true; + } + catch(InvalidRegistryException& ) + { + } + + return false; +} + + +void SAL_CALL NestedRegistryImpl::close( ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( m_localReg.is() && m_localReg->isValid() ) + { + m_localReg->close(); + } + if ( m_defaultReg.is() && m_defaultReg->isValid() ) + { + m_defaultReg->close(); + } +} + + +void SAL_CALL NestedRegistryImpl::destroy( ) +{ + throw InvalidRegistryException( + "the 'destroy' method is not specified for a nested registry" ); +} + + +Reference< XRegistryKey > SAL_CALL NestedRegistryImpl::getRootKey( ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( !m_localReg.is() || !m_localReg->isValid() ) + { + throw InvalidRegistryException(); + } + + Reference<XRegistryKey> localKey, defaultKey; + + localKey = m_localReg->getRootKey(); + + if ( localKey.is() ) + { + if ( m_defaultReg.is() && m_defaultReg->isValid() ) + { + defaultKey = m_defaultReg->getRootKey(); + } + + return new NestedKeyImpl(this, localKey, defaultKey); + } + + return Reference<XRegistryKey>(); +} + + +sal_Bool SAL_CALL NestedRegistryImpl::isReadOnly( ) +{ + Guard< Mutex > aGuard( m_mutex ); + try + { + if ( m_localReg.is() && m_localReg->isValid() ) + return m_localReg->isReadOnly(); + } + catch(InvalidRegistryException& ) + { + } + + return false; +} + + +void SAL_CALL NestedRegistryImpl::mergeKey( const OUString&, const OUString& ) +{ + throw css::uno::RuntimeException("css.registry.NestedRegistry::mergeKey: not implemented"); +} + +} // namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_NestedRegistry_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new NestedRegistryImpl); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/implementationregistration/implreg.cxx b/stoc/source/implementationregistration/implreg.cxx new file mode 100644 index 0000000000..ca8c6b2673 --- /dev/null +++ b/stoc/source/implementationregistration/implreg.cxx @@ -0,0 +1,1580 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> +#include <string_view> +#include <vector> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> +#include <rtl/ustring.hxx> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/registry/XImplementationRegistration2.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/reflection/XServiceTypeDescription.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include "mergekeys.hxx" + +#if defined(_WIN32) +#include <io.h> +#endif + + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::loader; +using namespace css::beans; +using namespace css::lang; +using namespace css::registry; +using namespace cppu; +using namespace osl; + +namespace { + +constexpr OUString slash_UNO_slash_REGISTRY_LINKS + = u"/UNO/REGISTRY_LINKS"_ustr; +constexpr OUString slash_IMPLEMENTATIONS + = u"/IMPLEMENTATIONS"_ustr; +constexpr OUString slash_UNO + = u"/UNO"_ustr; +constexpr OUString slash_UNO_slash_SERVICES + = u"/UNO/SERVICES"_ustr; +constexpr OUString slash_UNO_slash_SINGLETONS + = u"/UNO/SINGLETONS"_ustr; +constexpr OUString slash_SERVICES + = u"/SERVICES/"_ustr; +constexpr OUString slash_UNO_slash_LOCATION + = u"/UNO/LOCATION"_ustr; +constexpr OUString slash_UNO_slash_ACTIVATOR + = u"/UNO/ACTIVATOR"_ustr; +constexpr OUString colon_old + = u":old"_ustr; +constexpr OUStringLiteral com_sun_star_registry_SimpleRegistry + = u"com.sun.star.registry.SimpleRegistry"; +constexpr OUString Registry + = u"Registry"_ustr; + +// static deleteAllLinkReferences() + +void deleteAllLinkReferences(const Reference < XSimpleRegistry >& xReg, + const Reference < XRegistryKey >& xSource) + // throw ( InvalidRegistryException, RuntimeException ) +{ + Reference < XRegistryKey > xKey = xSource->openKey( + slash_UNO_slash_REGISTRY_LINKS ); + + if (!(xKey.is() && (xKey->getValueType() == RegistryValueType_ASCIILIST))) + return; + + const Sequence<OUString> linkNames = xKey->getAsciiListValue(); + + if (!linkNames.hasElements()) + return; + + OUString aLinkName; + OUString aLinkParent; + Reference < XRegistryKey > xLinkParent; + const sal_Unicode* pTmpName = nullptr; + const sal_Unicode* pShortName = nullptr; + sal_Int32 sEnd = 0; + + for (const OUString& rLinkName : linkNames) + { + aLinkName = rLinkName; + + pTmpName = aLinkName.getStr(); + + if (pTmpName[0] != L'/') + continue; + + sal_Int32 nIndex = rtl_ustr_indexOfChar( pTmpName, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName = pTmpName+nIndex; + + while (pShortName && pShortName[1] == L'%') + { + nIndex = rtl_ustr_indexOfChar( pShortName+2, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName += nIndex+2; + } + + if (pShortName) + { + aLinkName = aLinkName.copy(0, pShortName - pTmpName); + } + + xReg->getRootKey()->deleteLink(aLinkName); + + sEnd = aLinkName.lastIndexOf( '/' ); + + aLinkParent = aLinkName.copy(0, sEnd); + + while(!aLinkParent.isEmpty()) + { + xLinkParent = xReg->getRootKey()->openKey(aLinkParent); + + if (xLinkParent.is() && !xLinkParent->getKeyNames().hasElements()) + { + aLinkName = aLinkParent; + + xReg->getRootKey()->deleteKey(aLinkParent); + + sEnd = aLinkName.lastIndexOf( '/' ); + + aLinkParent = aLinkName.copy(0, sEnd); + } + else + { + break; + } + } + } +} + + +// static prepareLink + +void prepareLink( const Reference < XSimpleRegistry > & xDest, + const Reference < XRegistryKey > & xSource, + const OUString& link) + // throw ( InvalidRegistryException, RuntimeException ) +{ + OUString linkRefName = xSource->getKeyName(); + OUString linkName(link); + bool isRelativ = false; + + const sal_Unicode* pTmpName = link.getStr(); + const sal_Unicode* pShortName; + sal_Int32 nIndex = rtl_ustr_indexOfChar( pTmpName, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName = pTmpName+nIndex; + + if (pTmpName[0] != L'/') + isRelativ = true; + + while (pShortName && pShortName[1] == L'%') + { + nIndex = rtl_ustr_indexOfChar( pShortName+2, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName += nIndex+2; + } + + if (pShortName) + { + linkRefName += link.subView(pShortName - pTmpName + 1); + linkName = link.copy(0, pShortName - pTmpName); + } + + if (isRelativ) + xSource->createLink(linkName, linkRefName); + else + xDest->getRootKey()->createLink(linkName, linkRefName); +} + + +// static searchImplForLink + +OUString searchImplForLink( + const Reference < XRegistryKey > & xRootKey, + std::u16string_view linkName, + std::u16string_view implName ) + // throw ( InvalidRegistryException, RuntimeException ) +{ + Reference < XRegistryKey > xKey = xRootKey->openKey( slash_IMPLEMENTATIONS ); + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys( xKey->openKeys() ); + OUString key_name( slash_UNO + linkName ); + + for (const Reference < XRegistryKey >& xImplKey : subKeys) + { + try + { + if (xImplKey->getKeyType( key_name ) == RegistryKeyType_LINK) + { + OUString oldImplName = xImplKey->getKeyName().copy(strlen("/IMPLEMENTATIONS/")); + if (implName != oldImplName) + { + return oldImplName; + } + } + } + catch(InvalidRegistryException&) + { + } + } + } + + return OUString(); +} + + +// static searchLinkTargetForImpl + +OUString searchLinkTargetForImpl(const Reference < XRegistryKey >& xRootKey, + std::u16string_view linkName, + const OUString& implName) +{ + Reference < XRegistryKey > xKey = xRootKey->openKey( slash_IMPLEMENTATIONS ); + + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys = xKey->openKeys(); + + OUString qualifiedLinkName( slash_UNO + linkName ); + + auto pSubKey = std::find_if(subKeys.begin(), subKeys.end(), + [&implName, &qualifiedLinkName](const Reference<XRegistryKey>& rSubKey) { + OUString tmpImplName = rSubKey->getKeyName().copy(strlen("/IMPLEMENTATIONS/")); + return tmpImplName == implName + && rSubKey->getKeyType( qualifiedLinkName ) == RegistryKeyType_LINK; + }); + if (pSubKey != subKeys.end()) + return (*pSubKey)->getLinkTarget( qualifiedLinkName ); + } + + return OUString(); +} + + +// static createUniqueSubEntry + +void createUniqueSubEntry(const Reference < XRegistryKey > & xSuperKey, + const OUString& value) + // throw ( InvalidRegistryException, RuntimeException ) +{ + if (!xSuperKey.is()) + return; + + if (xSuperKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xSuperKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + + bool bReady = comphelper::findValue(implEntries, value) != -1; + + if (bReady) + { + Sequence<OUString> implEntriesNew(length); + auto it = implEntriesNew.getArray(); + *it = value; + + std::copy_if(implEntries.begin(), implEntries.end(), std::next(it), + [&value](const OUString& rEntry) { return rEntry != value; }); + xSuperKey->setAsciiListValue(implEntriesNew); + } else + { + Sequence<OUString> implEntriesNew(length+1); + auto it = implEntriesNew.getArray(); + *it = value; + + std::copy(implEntries.begin(), implEntries.end(), std::next(it)); + xSuperKey->setAsciiListValue(implEntriesNew); + } + } else + { + Sequence<OUString> implEntriesNew { value }; + + xSuperKey->setAsciiListValue(implEntriesNew); + } +} + + +// static deleteSubEntry + +bool deleteSubEntry(const Reference < XRegistryKey >& xSuperKey, const OUString& value) + // throw ( InvalidRegistryException, RuntimeException ) +{ + if (xSuperKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xSuperKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + sal_Int32 equals = static_cast<sal_Int32>(std::count(implEntries.begin(), implEntries.end(), value)); + bool hasNoImplementations = false; + + if (equals == length) + { + hasNoImplementations = true; + } else + { + Sequence<OUString> implEntriesNew(length - equals); + + std::copy_if(implEntries.begin(), implEntries.end(), implEntriesNew.getArray(), + [&value](const OUString& rEntry) { return rEntry != value; }); + xSuperKey->setAsciiListValue(implEntriesNew); + } + + if (hasNoImplementations) + { + return true; + } + } + return false; +} + + +// static prepareUserLink + +void prepareUserLink(const Reference < XSimpleRegistry >& xDest, + const OUString& linkName, + const OUString& linkTarget, + std::u16string_view implName) +{ + Reference < XRegistryKey > xRootKey = xDest->getRootKey(); + + if (xRootKey->getKeyType(linkName) == RegistryKeyType_LINK) + { + OUString oldImplName(searchImplForLink(xRootKey, linkName, implName)); + + if (!oldImplName.isEmpty()) + { + createUniqueSubEntry(xDest->getRootKey()->createKey( + linkName + colon_old ), oldImplName); + } + } + + if (xRootKey->isValid()) + xRootKey->createLink(linkName, linkTarget); +} + + +// static deleteUserLink + +void deletePathIfPossible(const Reference < XRegistryKey >& xRootKey, + const OUString& path) +{ + try + { + Sequence<OUString> keyNames(xRootKey->openKey(path)->getKeyNames()); + + if (!keyNames.hasElements() && + xRootKey->openKey(path)->getValueType() == RegistryValueType_NOT_DEFINED) + { + xRootKey->deleteKey(path); + + OUString newPath = path.copy(0, path.lastIndexOf('/')); + + if (newPath.getLength() > 1) + deletePathIfPossible(xRootKey, newPath); + } + } + catch(InvalidRegistryException&) + { + } +} + + +// static deleteUserLink + +void deleteUserLink(const Reference < XRegistryKey >& xRootKey, + const OUString& linkName, + std::u16string_view linkTarget, + const OUString& implName) + // throw ( InvalidRegistryException, RuntimeException ) +{ + bool bClean = false; + + if (xRootKey->getKeyType(linkName) == RegistryKeyType_LINK) + { + OUString tmpTarget = xRootKey->getLinkTarget(linkName); + + if (tmpTarget == linkTarget) + { + xRootKey->deleteLink(linkName); + } + } + + Reference < XRegistryKey > xOldKey = xRootKey->openKey( + linkName + colon_old ); + if (xOldKey.is()) + { + if (xOldKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xOldKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + sal_Int32 equals = static_cast<sal_Int32>(std::count(implEntries.begin(), implEntries.end(), implName)); + bool hasNoImplementations = false; + + if (equals == length) + { + hasNoImplementations = true; + } else + { + OUString oldImpl; + + if (length > equals + 1) + { + Sequence<OUString> implEntriesNew(length - equals - 1); + auto pNewArray = implEntriesNew.getArray(); + + sal_Int32 j = 0; + bool first = true; + for (sal_Int32 i = 0; i < length; i++) + { + if (implEntries[i] != implName) + { + if (first) + { + oldImpl = implEntries[i]; + first = false; + } else + { + pNewArray[j++] = implEntries[i]; + } + } + } + + xOldKey->setAsciiListValue(implEntriesNew); + } else + { + oldImpl = implEntries[0]; + OUString path(xOldKey->getKeyName()); + xOldKey->closeKey(); + xRootKey->deleteKey(path); + } + + OUString oldTarget = searchLinkTargetForImpl(xRootKey, linkName, oldImpl); + if (!oldTarget.isEmpty()) + { + xRootKey->createLink(linkName, oldTarget); + } + } + + if (hasNoImplementations) + { + bClean = true; + OUString path(xOldKey->getKeyName()); + xOldKey->closeKey(); + xRootKey->deleteKey(path); + } + } + } else + { + bClean = true; + } + + if (bClean) + { + OUString path = linkName.copy(0, linkName.lastIndexOf('/')); + deletePathIfPossible(xRootKey, path); + } +} + + +// static prepareUserKeys + +void prepareUserKeys(const Reference < XSimpleRegistry >& xDest, + const Reference < XRegistryKey >& xUnoKey, + const Reference < XRegistryKey >& xKey, + const OUString& implName, + bool bRegister) +{ + bool hasSubKeys = false; + + Sequence<OUString> keyNames = xKey->getKeyNames(); + + OUString relativKey; + if (keyNames.hasElements()) + relativKey = keyNames.getConstArray()[0].copy(xKey->getKeyName().getLength()+1); + + if (keyNames.getLength() == 1 && + xKey->getKeyType(relativKey) == RegistryKeyType_LINK) + { + hasSubKeys = true; + + OUString linkTarget = xKey->getLinkTarget(relativKey); + OUString linkName( + OUString::Concat(xKey->getKeyName().subView(xUnoKey->getKeyName().getLength())) + + "/" + relativKey); + + if (bRegister) + { + prepareUserLink(xDest, linkName, linkTarget, implName); + } else + { + deleteUserLink(xDest->getRootKey(), linkName, linkTarget, implName); + } + } else + { + const Sequence< Reference < XRegistryKey> > subKeys = xKey->openKeys(); + + if (subKeys.hasElements()) + { + hasSubKeys = true; + + for (const Reference < XRegistryKey > & rSubKey : subKeys) + { + prepareUserKeys(xDest, xUnoKey, rSubKey, implName, bRegister); + } + } + } + + if (hasSubKeys) + return; + + OUString keyName(xKey->getKeyName().copy(xUnoKey->getKeyName().getLength())); + + Reference < XRegistryKey > xRootKey = xDest->getRootKey(); + if (bRegister) + { + createUniqueSubEntry(xRootKey->createKey(keyName), implName); + } + else + { + Reference< XRegistryKey > rKey = xRootKey->openKey(keyName); + if( rKey.is() ) + { + deleteSubEntry(rKey, implName); + xRootKey->deleteKey(keyName); + } + + OUString path = keyName.copy(0, keyName.lastIndexOf('/')); + if( !path.isEmpty() ) + { + deletePathIfPossible(xRootKey, path); + } + } +} + + +// static deleteAllImplementations + +void deleteAllImplementations( const Reference < XSimpleRegistry >& xReg, + const Reference < XRegistryKey >& xSource, + std::u16string_view locationUrl, + std::vector<OUString> & implNames) + // throw (InvalidRegistryException, RuntimeException) +{ + Sequence < Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + if (subKeys.hasElements()) + { + bool hasLocationUrl = false; + + for (const Reference < XRegistryKey> & xImplKey : std::as_const(subKeys)) + { + Reference < XRegistryKey > xKey = xImplKey->openKey( + slash_UNO_slash_LOCATION ); + + if (xKey.is() && (xKey->getValueType() == RegistryValueType_ASCII)) + { + if (xKey->getAsciiValue() == locationUrl) + { + hasLocationUrl = true; + + OUString implName(xImplKey->getKeyName().copy(1)); + sal_Int32 firstDot = implName.indexOf('/'); + + if (firstDot >= 0) + implName = implName.copy(firstDot + 1); + + implNames.push_back(implName); + + deleteAllLinkReferences(xReg, xImplKey); + + xKey = xImplKey->openKey( slash_UNO ); + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys2 = xKey->openKeys(); + + for (const Reference < XRegistryKey > & rSubKey2 : subKeys2) + { + if (rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_SERVICES ) && + rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_REGISTRY_LINKS ) && + rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_ACTIVATOR ) && + rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_SINGLETONS ) && + rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_LOCATION) ) + { + prepareUserKeys(xReg, xKey, rSubKey2, implName, false); + } + } + } + } + } + + if (hasLocationUrl) + { + hasLocationUrl = false; + OUString path(xImplKey->getKeyName()); + xImplKey->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } + + subKeys = xSource->openKeys(); + if (!subKeys.hasElements()) + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } else + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } +} + + +void delete_all_singleton_entries( + Reference < registry::XRegistryKey > const & xSingletons_section, + ::std::vector< OUString > const & impl_names ) + // throw (InvalidRegistryException, RuntimeException) +{ + Sequence< Reference< registry::XRegistryKey > > singletons( xSingletons_section->openKeys() ); + Reference< registry::XRegistryKey > const * subkeys = singletons.getConstArray(); + for ( sal_Int32 nPos = singletons.getLength(); nPos--; ) + { + Reference< registry::XRegistryKey > const & xSingleton = subkeys[ nPos ]; + Reference< registry::XRegistryKey > xRegisteredImplNames( + xSingleton->openKey( "REGISTERED_BY" ) ); + if (xRegisteredImplNames.is() && xRegisteredImplNames->isValid()) + { + Sequence< OUString > registered_implnames; + try + { + registered_implnames = xRegisteredImplNames->getAsciiListValue(); + } + catch (registry::InvalidValueException &) + { + } + auto aNonConstRange = asNonConstRange(registered_implnames); + sal_Int32 nOrigRegLength = registered_implnames.getLength(); + sal_Int32 nNewLength = nOrigRegLength; + for ( sal_Int32 n = nOrigRegLength; n--; ) + { + OUString const & registered_implname = registered_implnames[ n ]; + + for (auto const& impl_name : impl_names) + { + if (impl_name == registered_implname) + { + aNonConstRange[ n ] = registered_implnames[ nNewLength -1 ]; + --nNewLength; + } + } + } + + if (nNewLength != nOrigRegLength) + { + if (0 == nNewLength) + { + // remove whole entry + xRegisteredImplNames->closeKey(); + xSingleton->deleteKey( "REGISTERED_BY" ); + // registry key cannot provide its relative name, only absolute :( + OUString abs( xSingleton->getKeyName() ); + xSingletons_section->deleteKey( abs.copy( abs.lastIndexOf( '/' ) +1 ) ); + } + else + { + registered_implnames.realloc( nNewLength ); + xRegisteredImplNames->setAsciiListValue( registered_implnames ); + } + } + } + } +} + + +// static deleteAllServiceEntries + +void deleteAllServiceEntries( const Reference < XSimpleRegistry >& xReg, + const Reference < XRegistryKey >& xSource, + const OUString& implName) + // throw ( InvalidRegistryException, RuntimeException ) +{ + Sequence< Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + if (subKeys.hasElements()) + { + bool hasNoImplementations = false; + + for (const Reference < XRegistryKey > & xServiceKey : std::as_const(subKeys)) + { + if (xServiceKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xServiceKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + sal_Int32 equals = static_cast<sal_Int32>(std::count(implEntries.begin(), implEntries.end(), implName)); + + if (equals == length) + { + hasNoImplementations = true; + } else + { + if (equals > 0) + { + Sequence<OUString> implEntriesNew(length-equals); + + std::copy_if(implEntries.begin(), implEntries.end(), implEntriesNew.getArray(), + [&implName](const OUString& rEntry) { return rEntry != implName; }); + + xServiceKey->setAsciiListValue(implEntriesNew); + } + } + } + + if (hasNoImplementations) + { + hasNoImplementations = false; + OUString path(xServiceKey->getKeyName()); + xServiceKey->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } + + subKeys = xSource->openKeys(); + if (!subKeys.hasElements()) + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } else + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } +} + + +bool is_supported_service( + OUString const & service_name, + Reference< reflection::XServiceTypeDescription > const & xService_td ) +{ + if (xService_td->getName() == service_name) + return true; + const Sequence< Reference< reflection::XServiceTypeDescription > > seq( + xService_td->getMandatoryServices() ); + return std::any_of(seq.begin(), seq.end(), [&service_name](const auto& rService) { + return is_supported_service( service_name, rService ); }); +} + + +void insert_singletons( + Reference< registry::XSimpleRegistry > const & xDest, + Reference< registry::XRegistryKey > const & xImplKey, + Reference< XComponentContext > const & xContext ) + // throw( registry::InvalidRegistryException, registry::CannotRegisterImplementationException, RuntimeException ) +{ + // singletons + Reference< registry::XRegistryKey > xKey( xImplKey->openKey( "UNO/SINGLETONS" ) ); + if (!(xKey.is() && xKey->isValid())) + return; + + OUString implname( xImplKey->getKeyName().copy( sizeof ("/IMPLEMENTATIONS/") -1 ) ); + // singleton entries + Sequence< Reference< registry::XRegistryKey > > xSingletons_section( xKey->openKeys() ); + Reference< registry::XRegistryKey > const * p = xSingletons_section.getConstArray(); + for ( sal_Int32 nPos = xSingletons_section.getLength(); nPos--; ) + { + Reference< registry::XRegistryKey > const & xSingleton = p[ nPos ]; + OUString singleton_name( + xSingleton->getKeyName().copy( + implname.getLength() + sizeof ("/IMPLEMENTATIONS//UNO/SINGLETONS/") -1 ) ); + OUString service_name( xSingleton->getStringValue() ); + + OUString keyname( "/SINGLETONS/" + singleton_name ); + Reference< registry::XRegistryKey > xKey2( xDest->getRootKey()->openKey( keyname ) ); + if (xKey2.is() && xKey2->isValid()) + { + try + { + OUString existing_name( xKey2->getStringValue() ); + if ( existing_name != service_name ) + { + Reference< container::XHierarchicalNameAccess > xTDMgr; + OUString the_tdmgr = + "/singletons/com.sun.star.reflection.theTypeDescriptionManager"; + xContext->getValueByName( the_tdmgr ) >>= xTDMgr; + if (! xTDMgr.is()) + { + throw RuntimeException( "cannot get singleton " + the_tdmgr ); + } + try + { + Reference< reflection::XServiceTypeDescription > xExistingService_td; + xTDMgr->getByHierarchicalName( existing_name ) >>= xExistingService_td; + if (! xExistingService_td.is()) + { + throw RuntimeException( "cannot get service type description: " + existing_name ); + } + + // everything's fine if existing service entry supports the one + // to be registered + if (! is_supported_service( service_name, xExistingService_td )) + { + throw registry::CannotRegisterImplementationException( + "existing singleton service (" + singleton_name + "=" + existing_name + ") " + " does not support given one: " + service_name); + } + } + catch (const container::NoSuchElementException & exc) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "cannot get service type description: " + exc.Message, + nullptr, anyEx ); + } + } + } + catch (registry::InvalidValueException &) + { + // repair + xKey2->setStringValue( service_name ); + } + } + else + { + // insert singleton entry + xKey2 = xDest->getRootKey()->createKey( keyname ); + xKey2->setStringValue( service_name ); + } + + Reference< registry::XRegistryKey > xRegisteredImplNames( + xKey2->openKey( "REGISTERED_BY" ) ); + if (!xRegisteredImplNames.is() || !xRegisteredImplNames->isValid()) + { + // create + xRegisteredImplNames = xKey2->createKey( "REGISTERED_BY" ); + } + + Sequence< OUString > implnames; + try + { + implnames = xRegisteredImplNames->getAsciiListValue(); + } + catch (registry::InvalidValueException &) + { + } + // check implname is already in + if (comphelper::findValue(implnames, implname) == -1) + { + // append and write back + implnames.realloc( implnames.getLength() +1 ); + implnames.getArray()[ implnames.getLength() -1 ] = implname; + xRegisteredImplNames->setAsciiListValue( implnames ); + } + } +} + + +// static prepareRegistry + +void prepareRegistry( + const Reference < XSimpleRegistry >& xDest, + const Reference < XRegistryKey >& xSource, + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + Reference< XComponentContext > const & xContext ) + // throw ( InvalidRegistryException, CannotRegisterImplementationException, RuntimeException ) +{ + const Sequence< Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + if (!subKeys.hasElements()) + { + throw InvalidRegistryException( + "prepareRegistry(): source registry is empty" ); + } + + for (const Reference < XRegistryKey >& xImplKey : subKeys) + { + Reference < XRegistryKey > xKey = xImplKey->openKey( + slash_UNO_slash_SERVICES ); + + if (xKey.is()) + { + // update entries in SERVICES section + const Sequence< Reference < XRegistryKey > > serviceKeys = xKey->openKeys(); + + OUString implName = xImplKey->getKeyName().copy(1); + sal_Int32 firstDot = implName.indexOf('/'); + + if (firstDot >= 0) + implName = implName.copy(firstDot + 1); + + sal_Int32 offset = xKey->getKeyName().getLength() + 1; + + for (const Reference < XRegistryKey >& rServiceKey : serviceKeys) + { + OUString serviceName = rServiceKey->getKeyName().copy(offset); + + createUniqueSubEntry( + xDest->getRootKey()->createKey( + slash_SERVICES + serviceName ), + implName); + } + + xKey = xImplKey->openKey( slash_UNO ); + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys2 = xKey->openKeys(); + + for (const Reference < XRegistryKey >& rSubKey2 : subKeys2) + { + if (rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_SERVICES) && + rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_REGISTRY_LINKS ) && + rSubKey2->getKeyName() != Concat2View(xImplKey->getKeyName() + slash_UNO_slash_SINGLETONS )) + { + prepareUserKeys(xDest, xKey, rSubKey2, implName, true); + } + } + } + } + + // update LOCATION entry + xKey = xImplKey->createKey( slash_UNO_slash_LOCATION ); + + if (xKey.is()) + { + xKey->setAsciiValue(locationUrl); + } + + // update ACTIVATOR entry + xKey = xImplKey->createKey( slash_UNO_slash_ACTIVATOR ); + + if (xKey.is()) + { + xKey->setAsciiValue(implementationLoaderUrl); + } + + xKey = xImplKey->openKey( slash_UNO_slash_SERVICES ); + + if (xKey.is() && (xKey->getValueType() == RegistryValueType_ASCIILIST)) + { + // update link entries in REGISTRY_LINKS section + const Sequence<OUString> linkNames = xKey->getAsciiListValue(); + + for (const OUString& rLinkName : linkNames) + { + prepareLink(xDest, xImplKey, rLinkName); + } + } + + insert_singletons( xDest, xImplKey, xContext ); + } +} + + +void findImplementations( const Reference < XRegistryKey > & xSource, + std::vector<OUString>& implNames) +{ + bool isImplKey = false; + + try + { + Reference < XRegistryKey > xKey = xSource->openKey( + slash_UNO_slash_SERVICES ); + + if (xKey.is() && xKey->getKeyNames().hasElements()) + { + isImplKey = true; + + OUString implName = xSource->getKeyName().copy(1).replace('/', '.'); + sal_Int32 firstDot = implName.indexOf('.'); + + if (firstDot >= 0) + implName = implName.copy(firstDot + 1); + + implNames.push_back(implName); + } + } + catch(InvalidRegistryException&) + { + } + + if (isImplKey) return; + + try + { + const Sequence< Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + for (const Reference < XRegistryKey >& rSubKey : subKeys) + { + findImplementations(rSubKey, implNames); + } + } + catch(InvalidRegistryException&) + { + } +} + + +class ImplementationRegistration + : public WeakImplHelper< XImplementationRegistration2, XServiceInfo, XInitialization > +{ +public: + explicit ImplementationRegistration( const Reference < XComponentContext > & rSMgr ); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XImplementationRegistration + virtual void SAL_CALL registerImplementation( + const OUString& implementationLoader, + const OUString& location, + const Reference < XSimpleRegistry > & xReg) override; + + virtual sal_Bool SAL_CALL revokeImplementation( + const OUString& location, + const Reference < XSimpleRegistry >& xReg) override; + + virtual Sequence< OUString > SAL_CALL getImplementations( + const OUString& implementationLoader, + const OUString& location) override; + virtual Sequence< OUString > SAL_CALL checkInstantiation( + const OUString& implementationName) override; + + // XImplementationRegistration2 + virtual void SAL_CALL registerImplementationWithLocation( + const OUString& implementationLoader, + const OUString& location, + const OUString& registeredLocation, + const Reference < XSimpleRegistry > & xReg) override; + + // XInitialization + virtual void SAL_CALL initialize( + const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +private: // helper methods + void prepareRegister( + const OUString& implementationLoader, + const OUString& location, + const OUString& registeredLocation, + const Reference < XSimpleRegistry > & xReg); + // throw( CannotRegisterImplementationException, RuntimeException ) + + static void doRegister( const Reference < XMultiComponentFactory >& xSMgr, + const Reference < XComponentContext > &xCtx, + const Reference < XImplementationLoader >& xAct, + const Reference < XSimpleRegistry >& xDest, + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl); + /* throw ( InvalidRegistryException, + MergeConflictException, + CannotRegisterImplementationException, RuntimeException ) */ + + static void doRevoke( const Reference < XSimpleRegistry >& xDest, + std::u16string_view locationUrl ); + // throw( InvalidRegistryException, RuntimeException ) + Reference< XSimpleRegistry > getRegistryFromServiceManager() const; + + static Reference< XSimpleRegistry > createTemporarySimpleRegistry( + const Reference< XMultiComponentFactory > &rSMgr, + const Reference < XComponentContext > & rCtx ); + +private: // members + Reference < XMultiComponentFactory > m_xSMgr; + Reference < XComponentContext > m_xCtx; +}; + + +// ImplementationRegistration() + +ImplementationRegistration::ImplementationRegistration( const Reference < XComponentContext > & xCtx ) + : m_xSMgr( xCtx->getServiceManager() ) + , m_xCtx( xCtx ) +{} + +// XServiceInfo +OUString ImplementationRegistration::getImplementationName() +{ + return "com.sun.star.comp.stoc.ImplementationRegistration"; +} + +// XServiceInfo +sal_Bool ImplementationRegistration::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > ImplementationRegistration::getSupportedServiceNames() +{ + return { "com.sun.star.registry.ImplementationRegistration" }; +} + +Reference< XSimpleRegistry > ImplementationRegistration::getRegistryFromServiceManager() const +{ + Reference < XPropertySet > xPropSet( m_xSMgr, UNO_QUERY ); + Reference < XSimpleRegistry > xRegistry; + + if( xPropSet.is() ) { + + try { // the implementation does not support XIntrospectionAccess ! + + Any aAny = xPropSet->getPropertyValue( Registry ); + + if( aAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) { + aAny >>= xRegistry; + } + } + catch( UnknownPropertyException & ) { + // empty reference is error signal ! + } + } + + return xRegistry; +} + + +// XInitialization + +void ImplementationRegistration::initialize( + const css::uno::Sequence< css::uno::Any >& aArgs ) +{ + + if( aArgs.getLength() != 4 ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() expects 4 parameters, got " + OUString::number( aArgs.getLength() ), + Reference<XInterface > (), 0 ); + } + + Reference< XImplementationLoader > rLoader; + OUString loaderServiceName; + OUString locationUrl; + Reference< XSimpleRegistry > rReg; + + // 1st argument : An instance of an implementation loader + if( aArgs.getConstArray()[0].getValueType().getTypeClass() == TypeClass_INTERFACE ) { + aArgs.getConstArray()[0] >>= rLoader; + } + if( !rLoader.is()) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid first parameter," + "expected " + cppu::UnoType<decltype(rLoader)>::get().getTypeName() + + ", got " + aArgs.getConstArray()[0].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + + // 2nd argument : The service name of the loader. This name is written into the registry + if( aArgs.getConstArray()[1].getValueType().getTypeClass() == TypeClass_STRING ) { + aArgs.getConstArray()[1] >>= loaderServiceName; + } + if( loaderServiceName.isEmpty() ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid second parameter," + "expected string, got " + aArgs.getConstArray()[1].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + + // 3rd argument : The file name of the dll, that contains the loader + if( aArgs.getConstArray()[2].getValueType().getTypeClass() == TypeClass_STRING ) { + aArgs.getConstArray()[2] >>= locationUrl; + } + if( locationUrl.isEmpty() ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid third parameter," + "expected string, got " + aArgs.getConstArray()[2].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + + // 4th argument : The registry, the service should be written to + if( aArgs.getConstArray()[3].getValueType().getTypeClass() == TypeClass_INTERFACE ) { + aArgs.getConstArray()[3] >>= rReg; + } + + if( !rReg.is() ) { + rReg = getRegistryFromServiceManager(); + if( !rReg.is() ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid fourth parameter," + "expected " + cppu::UnoType<decltype(rReg)>::get().getTypeName() + + ", got " + aArgs.getConstArray()[3].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + } + + doRegister(m_xSMgr, m_xCtx, rLoader , rReg, loaderServiceName , locationUrl, locationUrl); +} + + +// virtual function registerImplementationWithLocation of XImplementationRegistration2 + +void ImplementationRegistration::registerImplementationWithLocation( + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl, + const Reference < XSimpleRegistry > & xReg) +{ + prepareRegister( + implementationLoaderUrl, locationUrl, registeredLocationUrl, xReg); +} + +// helper function +void ImplementationRegistration::prepareRegister( + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl, + const Reference < XSimpleRegistry > & xReg) + // throw( CannotRegisterImplementationException, RuntimeException ) +{ + OUString activatorName; + + if (!implementationLoaderUrl.isEmpty()) + { + activatorName = implementationLoaderUrl.getToken(0, ':'); + } else + { + // check locationUrl to find out what kind of loader is needed + // set implLoaderUrl + } + + if( !m_xSMgr.is() ) { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() " + "no componentcontext available to instantiate loader" ); + } + + try + { + Reference < XImplementationLoader > xAct( + m_xSMgr->createInstanceWithContext(activatorName, m_xCtx) , UNO_QUERY ); + if (!xAct.is()) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() - The service " + + activatorName + " cannot be instantiated" ); + } + + Reference < XSimpleRegistry > xRegistry; + + if (xReg.is()) + { + // registry supplied by user + xRegistry = xReg; + } + else + { + xRegistry = getRegistryFromServiceManager(); + } + + if ( xRegistry.is()) + { + doRegister(m_xSMgr, m_xCtx, xAct, xRegistry, implementationLoaderUrl, + locationUrl, registeredLocationUrl); + } + + } + catch( CannotRegisterImplementationException & ) + { + throw; + } + catch( const InvalidRegistryException & e ) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() " + "InvalidRegistryException during registration (" + e.Message + ")" ); + } + catch( const MergeConflictException & e ) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() " + "MergeConflictException during registration (" + e.Message + ")" ); + } + +} + + +// virtual function registerImplementation of XImplementationRegistration + +void ImplementationRegistration::registerImplementation( + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const Reference < XSimpleRegistry > & xReg) +{ + prepareRegister(implementationLoaderUrl, locationUrl, locationUrl, xReg); +} + + +// virtual function revokeImplementation of XImplementationRegistration + +sal_Bool ImplementationRegistration::revokeImplementation(const OUString& location, + const Reference < XSimpleRegistry >& xReg) +{ + bool ret = false; + + Reference < XSimpleRegistry > xRegistry; + + if (xReg.is()) { + xRegistry = xReg; + } + else { + Reference < XPropertySet > xPropSet( m_xSMgr, UNO_QUERY ); + if( xPropSet.is() ) { + try { + Any aAny = xPropSet->getPropertyValue( Registry ); + + if( aAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) + { + aAny >>= xRegistry; + } + } + catch ( UnknownPropertyException & ) { + } + } + } + + if (xRegistry.is()) + { + try + { + doRevoke(xRegistry, location); + ret = true; + } + catch( InvalidRegistryException & ) + { + // no way to transport the error, as no exception is specified and a runtime + // exception is not appropriate. + OSL_FAIL( "InvalidRegistryException during revokeImplementation" ); + } + } + + return ret; +} + + +// virtual function getImplementations of XImplementationRegistration + +Sequence< OUString > ImplementationRegistration::getImplementations( + const OUString & implementationLoaderUrl, + const OUString & locationUrl) +{ + OUString activatorName; + + if (!implementationLoaderUrl.isEmpty()) + { + activatorName = implementationLoaderUrl.getToken(0, ':'); + } else + { + // check locationUrl to find out what kind of loader is needed + // set implementationLoaderUrl + } + + if( m_xSMgr.is() ) { + + Reference < XImplementationLoader > xAct( + m_xSMgr->createInstanceWithContext( activatorName, m_xCtx ), UNO_QUERY ); + + if (xAct.is()) + { + + Reference < XSimpleRegistry > xReg = + createTemporarySimpleRegistry( m_xSMgr, m_xCtx); + + if (xReg.is()) + { + try + { + xReg->open(OUString() /* in mem */, false, true); + Reference < XRegistryKey > xImpl; + + { // only necessary for deleting the temporary variable of rootkey + xImpl = xReg->getRootKey()->createKey( slash_IMPLEMENTATIONS ); + } + if (xAct->writeRegistryInfo(xImpl, implementationLoaderUrl, locationUrl)) + { + std::vector<OUString> implNames; + + findImplementations(xImpl, implNames); + + if (!implNames.empty()) + { + Sequence<OUString> seqImpl(comphelper::containerToSequence(implNames)); + xImpl->closeKey(); + return seqImpl; + } + } + + xImpl->closeKey(); + } + catch(MergeConflictException&) + { + } + catch(InvalidRegistryException&) + { + } + } + } + } + + return Sequence<OUString>(); +} + + +// virtual function checkInstantiation of XImplementationRegistration + +Sequence< OUString > ImplementationRegistration::checkInstantiation(const OUString&) +{ + OSL_FAIL( "ImplementationRegistration::checkInstantiation not implemented" ); + return Sequence<OUString>(); +} + + +// helper function doRegistration + + +void ImplementationRegistration::doRevoke( + const Reference < XSimpleRegistry >& xDest, + std::u16string_view locationUrl) + // throw ( InvalidRegistryException, RuntimeException ) +{ + if( !xDest.is() ) + return; + + std::vector<OUString> aNames; + + Reference < XRegistryKey > xRootKey( xDest->getRootKey() ); + + Reference < XRegistryKey > xKey = + xRootKey->openKey( slash_IMPLEMENTATIONS ); + if (xKey.is() && xKey->isValid()) + { + deleteAllImplementations(xDest, xKey, locationUrl, aNames); + } + + xKey = xRootKey->openKey( slash_SERVICES ); + if (xKey.is()) + { + for (auto const& name : aNames) + { + deleteAllServiceEntries(xDest, xKey, name); + } + } + + xKey = xRootKey->openKey( "/SINGLETONS" ); + if (xKey.is() && xKey->isValid()) + { + delete_all_singleton_entries( xKey, aNames ); + } + + if (xRootKey.is()) + xRootKey->closeKey(); + if (xKey.is() && xKey->isValid() ) + xKey->closeKey(); +} + +void ImplementationRegistration::doRegister( + const Reference< XMultiComponentFactory > & xSMgr, + const Reference< XComponentContext > &xCtx, + const Reference < XImplementationLoader > & xAct, + const Reference < XSimpleRegistry >& xDest, + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl) + /* throw ( InvalidRegistryException, + MergeConflictException, + CannotRegisterImplementationException, RuntimeException ) */ +{ + Reference < XSimpleRegistry > xReg = + createTemporarySimpleRegistry( xSMgr, xCtx ); + Reference < XRegistryKey > xSourceKey; + + if (!(xAct.is() && xReg.is() && xDest.is())) + return; + + try + { + xReg->open(OUString() /* in mem */, false, true); + + { // only necessary for deleting the temporary variable of rootkey + xSourceKey = xReg->getRootKey()->createKey( slash_IMPLEMENTATIONS ); + } + + bool bSuccess = + xAct->writeRegistryInfo(xSourceKey, implementationLoaderUrl, locationUrl); + if ( !bSuccess ) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::doRegistration() component registration signaled failure" ); + } + + prepareRegistry(xDest, xSourceKey, implementationLoaderUrl, registeredLocationUrl, xCtx); + + xSourceKey->closeKey(); + + xSourceKey = xReg->getRootKey(); + Reference < XRegistryKey > xDestKey = xDest->getRootKey(); + stoc_impreg::mergeKeys( xDestKey, xSourceKey ); + xDestKey->closeKey(); + xSourceKey->closeKey(); + + + // Cleanup Source registry. + if ( xSourceKey->isValid() ) + xSourceKey->closeKey(); + } + catch(CannotRegisterImplementationException&) + { + if ( xSourceKey->isValid() ) + xSourceKey->closeKey(); + // and throw again + throw; + } +} + + +Reference< XSimpleRegistry > ImplementationRegistration::createTemporarySimpleRegistry( + const Reference< XMultiComponentFactory > &rSMgr, + const Reference < XComponentContext > & xCtx) +{ + + Reference < XSimpleRegistry > xReg( + rSMgr->createInstanceWithContext( + com_sun_star_registry_SimpleRegistry, xCtx ), + UNO_QUERY); + OSL_ASSERT( xReg.is() ); + return xReg; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_ImplementationRegistration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ImplementationRegistration(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/implementationregistration/mergekeys.cxx b/stoc/source/implementationregistration/mergekeys.cxx new file mode 100644 index 0000000000..dde219b9ad --- /dev/null +++ b/stoc/source/implementationregistration/mergekeys.cxx @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <utility> +#include <vector> + +#include <osl/diagnose.h> + +#include <com/sun/star/registry/XRegistryKey.hpp> + +#include "mergekeys.hxx" + +using namespace ::osl; +using namespace css::uno; +using namespace ::com::sun::star; + +namespace stoc_impreg +{ + +namespace { + +struct Link +{ + OUString m_name; + OUString m_target; + + Link( OUString name, OUString target ) + : m_name(std::move( name )) + , m_target(std::move( target )) + {} +}; + +} + +typedef ::std::vector< Link > t_links; + + +static void mergeKeys( + Reference< registry::XRegistryKey > const & xDest, + Reference< registry::XRegistryKey > const & xSource, + t_links & links ) + // throw( registry::InvalidRegistryException, registry::MergeConflictException, RuntimeException ) +{ + if (!xSource.is() || !xSource->isValid()) { + throw registry::InvalidRegistryException( + "source key is null or invalid!" ); + } + if (!xDest.is() || !xDest->isValid()) { + throw registry::InvalidRegistryException( + "destination key is null or invalid!" ); + } + + // write value + switch (xSource->getValueType()) + { + case registry::RegistryValueType_NOT_DEFINED: + break; + case registry::RegistryValueType_LONG: + xDest->setLongValue( xSource->getLongValue() ); + break; + case registry::RegistryValueType_ASCII: + xDest->setAsciiValue( xSource->getAsciiValue() ); + break; + case registry::RegistryValueType_STRING: + xDest->setStringValue( xSource->getStringValue() ); + break; + case registry::RegistryValueType_BINARY: + xDest->setBinaryValue( xSource->getBinaryValue() ); + break; + case registry::RegistryValueType_LONGLIST: + xDest->setLongListValue( xSource->getLongListValue() ); + break; + case registry::RegistryValueType_ASCIILIST: + xDest->setAsciiListValue( xSource->getAsciiListValue() ); + break; + case registry::RegistryValueType_STRINGLIST: + xDest->setStringListValue( xSource->getStringListValue() ); + break; + default: + OSL_ASSERT(false); + break; + } + + // sub keys + Sequence< OUString > sourceKeys( xSource->getKeyNames() ); + OUString const * pSourceKeys = sourceKeys.getConstArray(); + for ( sal_Int32 nPos = sourceKeys.getLength(); nPos--; ) + { + // key name + OUString name( pSourceKeys[ nPos ] ); + sal_Int32 nSlash = name.lastIndexOf( '/' ); + if (nSlash >= 0) + { + name = name.copy( nSlash +1 ); + } + + if (xSource->getKeyType( name ) == registry::RegistryKeyType_KEY) + { + // try to open existing dest key or create new one + Reference< registry::XRegistryKey > xDestKey( xDest->createKey( name ) ); + Reference< registry::XRegistryKey > xSourceKey( xSource->openKey( name ) ); + mergeKeys( xDestKey, xSourceKey, links ); + xSourceKey->closeKey(); + xDestKey->closeKey(); + } + else // link + { + // remove existing key + Reference< registry::XRegistryKey > xDestKey( xDest->openKey( name ) ); + if (xDestKey.is() && xDestKey->isValid()) // something to remove + { + xDestKey->closeKey(); + if (xDest->getKeyType( name ) == registry::RegistryKeyType_LINK) + { + xDest->deleteLink( name ); + } + else + { + xDest->deleteKey( name ); + } + } + + links.push_back( Link( + pSourceKeys[ nPos ], // abs path + xSource->getResolvedName( name ) // abs resolved name + ) ); + } + } +} + + +void mergeKeys( + Reference< registry::XRegistryKey > const & xDest, + Reference< registry::XRegistryKey > const & xSource ) + // throw( registry::InvalidRegistryException, registry::MergeConflictException, RuntimeException ) +{ + if (!xDest.is() || !xDest->isValid()) { + throw registry::InvalidRegistryException( + "destination key is null or invalid!" ); + } + if (xDest->isReadOnly()) + { + throw registry::InvalidRegistryException( + "destination registry is read-only! cannot merge!" ); + } + + t_links links; + links.reserve( 16 ); + mergeKeys( xDest, xSource, links ); + + for ( size_t nPos = links.size(); nPos--; ) + { + Link const & r = links[ nPos ]; + OSL_VERIFY( xDest->createLink( r.m_name, r.m_target ) ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/implementationregistration/mergekeys.hxx b/stoc/source/implementationregistration/mergekeys.hxx new file mode 100644 index 0000000000..3f54e57124 --- /dev/null +++ b/stoc/source/implementationregistration/mergekeys.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STOC_SOURCE_IMPLEMENTATIONREGISTRATION_MERGEKEYS_HXX +#define INCLUDED_STOC_SOURCE_IMPLEMENTATIONREGISTRATION_MERGEKEYS_HXX + +#include <sal/config.h> +#include <sal/types.h> + +namespace com::sun::star::registry { + class XRegistryKey; +} +namespace com::sun::star::uno { template <typename > class Reference; } + +namespace stoc_impreg { + +// throws css::registry::InvalidRegistryException, +// css::registry::MergeConflictException, +// css::uno::RuntimeException: +void mergeKeys( + css::uno::Reference< css::registry::XRegistryKey > + const & xDest, + css::uno::Reference< css::registry::XRegistryKey > + const & xSource); + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/inspect/introspection.component b/stoc/source/inspect/introspection.component new file mode 100644 index 0000000000..84e121fad1 --- /dev/null +++ b/stoc/source/inspect/introspection.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.Introspection" + constructor="com_sun_star_comp_stoc_Introspection_get_implementation" + single-instance="true"> + <service name="com.sun.star.beans.Introspection"/> + <singleton name="com.sun.star.beans.theIntrospection"/> + </implementation> +</component> diff --git a/stoc/source/inspect/introspection.cxx b/stoc/source/inspect/introspection.cxx new file mode 100644 index 0000000000..eb06b66568 --- /dev/null +++ b/stoc/source/inspect/introspection.cxx @@ -0,0 +1,2420 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <limits> +#include <map> +#include <memory> +#include <mutex> +#include <set> + +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> +#include <salhelper/simplereferenceobject.hxx> + +#include <com/sun/star/lang/NoSuchMethodException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/reflection/XIdlClass.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/beans/XIntrospection.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> + +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <unordered_map> +#include <utility> + +using namespace css::uno; +using namespace css::lang; +using namespace css::reflection; +using namespace css::container; +using namespace css::beans; +using namespace css::beans::PropertyAttribute; +using namespace css::beans::PropertyConcept; +using namespace css::beans::MethodConcept; +using namespace cppu; +using namespace osl; + +namespace +{ + +typedef WeakImplHelper< XIntrospectionAccess, XMaterialHolder, XExactName, + XPropertySet, XFastPropertySet, XPropertySetInfo, + XNameContainer, XIndexContainer, XEnumerationAccess, + XIdlArray, XUnoTunnel > IntrospectionAccessHelper; + + +// Special value for Method-Concept, to be able to mark "normal" functions +#define MethodConcept_NORMAL_IMPL 0x80000000 + + +// Method to assert, if a class is derived from another class +bool isDerivedFrom( const Reference<XIdlClass>& xToTestClass, const Reference<XIdlClass>& xDerivedFromClass ) +{ + const Sequence< Reference<XIdlClass> > aClassesSeq = xToTestClass->getSuperclasses(); + + return std::any_of(aClassesSeq.begin(), aClassesSeq.end(), + [&xDerivedFromClass](const Reference<XIdlClass>& rxClass) { + return xDerivedFromClass->equals( rxClass ) + || isDerivedFrom( rxClass, xDerivedFromClass ); + }); +} + + +// *** Classification of Properties (no enum, to be able to use Sequence) *** +// Properties from a PropertySet-Interface +#define MAP_PROPERTY_SET 0 +// Properties from Fields +#define MAP_FIELD 1 +// Properties that get described with get/set methods +#define MAP_GETSET 2 +// Properties with only a set method +#define MAP_SETONLY 3 + + +// Increments by which the size of sequences get adjusted +#define ARRAY_SIZE_STEP 20 + + +//*** IntrospectionAccessStatic_Impl *** + +// Equals to the old IntrospectionAccessImpl, forms now a static +// part of the new Instance-related ImplIntrospectionAccess + +// Hashtable for the search of names +typedef std::unordered_map +< + OUString, + sal_Int32 +> +IntrospectionNameMap; + + +// Hashtable to assign exact names to the Lower-Case +// converted names, for the support of XExactName +typedef std::unordered_map +< + OUString, + OUString +> +LowerToExactNameMap; + + +class IntrospectionAccessStatic_Impl: public salhelper::SimpleReferenceObject +{ + friend class Implementation; + friend class ImplIntrospectionAccess; + + // Holding CoreReflection + Reference< XIdlReflection > mxCoreReflection; + + // InterfaceSequences, to save additional information in a property + // for example the Field at MAP_FIELD, the get/set-Methods at MAP_GETSET, et cetera + std::vector< Reference<XInterface> > aInterfaceSeq1; + std::vector< Reference<XInterface> > aInterfaceSeq2; + + // Hashtables for names + IntrospectionNameMap maPropertyNameMap; + IntrospectionNameMap maMethodNameMap; + LowerToExactNameMap maLowerToExactNameMap; + + // Vector of all Properties, also for delivering from getProperties() + std::vector<Property> maAllPropertySeq; + + // Mapping of properties to Access-Types + std::vector<sal_Int16> maMapTypeSeq; + + // Classification of found methods + std::vector<sal_Int32> maPropertyConceptSeq; + + // Number of Properties + sal_Int32 mnPropCount; + + // Number of Properties, which are assigned to particular concepts + //sal_Int32 mnDangerousPropCount; + sal_Int32 mnPropertySetPropCount; + sal_Int32 mnAttributePropCount; + sal_Int32 mnMethodPropCount; + + // Flags which indicate if various interfaces are present + bool mbFastPropSet; + bool mbElementAccess; + bool mbNameAccess; + bool mbNameReplace; + bool mbNameContainer; + bool mbIndexAccess; + bool mbIndexReplace; + bool mbIndexContainer; + bool mbEnumerationAccess; + bool mbIdlArray; + bool mbUnoTunnel; + + // Original handles of FastPropertySets + std::unique_ptr<sal_Int32[]> mpOrgPropertyHandleArray; + + // MethodSequence, that accepts all methods + std::vector< Reference<XIdlMethod> > maAllMethodSeq; + + // Classification of found methods + std::vector<sal_Int32> maMethodConceptSeq; + + // Number of methods + sal_Int32 mnMethCount; + + // Sequence of Listener, that can be registered + std::vector< Type > maSupportedListenerSeq; + + // Helper-methods for adjusting sizes of Sequences + void checkPropertyArraysSize( sal_Int32 iNextIndex ); + static void checkInterfaceArraySize( std::vector< Reference<XInterface> >& rSeq, std::vector<Reference<XInterface>>& rInterfaceVec, + sal_Int32 iNextIndex ); + +public: + explicit IntrospectionAccessStatic_Impl( Reference< XIdlReflection > const & xCoreReflection_ ); + sal_Int32 getPropertyIndex( const OUString& aPropertyName ) const; + sal_Int32 getMethodIndex( const OUString& aMethodName ) const; + + // Methods of XIntrospectionAccess (OLD, now only Impl) + void setPropertyValue(const Any& obj, const OUString& aPropertyName, const Any& aValue) const; +// void setPropertyValue(Any& obj, const OUString& aPropertyName, const Any& aValue) const; + Any getPropertyValue(const Any& obj, const OUString& aPropertyName) const; + void setPropertyValueByIndex(const Any& obj, sal_Int32 nIndex, const Any& aValue) const; +// void setPropertyValueByIndex(Any& obj, sal_Int32 nIndex, const Any& aValue) const; + Any getPropertyValueByIndex(const Any& obj, sal_Int32 nIndex) const; + + const std::vector<Property>& getProperties() const { return maAllPropertySeq; } + const std::vector< Reference<XIdlMethod> >& getMethods() const { return maAllMethodSeq; } + const std::vector< Type >& getSupportedListeners() const { return maSupportedListenerSeq; } + const std::vector<sal_Int32>& getPropertyConcepts() const { return maPropertyConceptSeq; } + const std::vector<sal_Int32>& getMethodConcepts() const { return maMethodConceptSeq; } +}; + + +// Ctor +IntrospectionAccessStatic_Impl::IntrospectionAccessStatic_Impl( Reference< XIdlReflection > const & xCoreReflection_ ) + : mxCoreReflection( xCoreReflection_ ) +{ + aInterfaceSeq1.resize( ARRAY_SIZE_STEP ); + aInterfaceSeq2.resize( ARRAY_SIZE_STEP ); + + // Property-Data + maAllPropertySeq.resize( ARRAY_SIZE_STEP ); + maMapTypeSeq.resize( ARRAY_SIZE_STEP ); + maPropertyConceptSeq.resize( ARRAY_SIZE_STEP ); + + mbFastPropSet = false; + mbElementAccess = false; + mbNameAccess = false; + mbNameReplace = false; + mbNameContainer = false; + mbIndexAccess = false; + mbIndexReplace = false; + mbIndexContainer = false; + mbEnumerationAccess = false; + mbIdlArray = false; + mbUnoTunnel = false; + + mpOrgPropertyHandleArray = nullptr; + + mnPropCount = 0; + //mnDangerousPropCount = 0; + mnPropertySetPropCount = 0; + mnAttributePropCount = 0; + mnMethodPropCount = 0; + + // Method-Data + mnMethCount = 0; +} + +sal_Int32 IntrospectionAccessStatic_Impl::getPropertyIndex( const OUString& aPropertyName ) const +{ + auto aIt = maPropertyNameMap.find(aPropertyName); + if (aIt != maPropertyNameMap.end()) + return aIt->second; + + return -1; +} + +sal_Int32 IntrospectionAccessStatic_Impl::getMethodIndex( const OUString& aMethodName ) const +{ + auto aIt = maMethodNameMap.find(aMethodName); + if (aIt != maMethodNameMap.end()) + { + return aIt->second; + } + + // #95159 Check if full qualified name matches + sal_Int32 nSearchFrom = aMethodName.getLength(); + while( true ) + { + // Strategy: Search back until the first '_' is found + sal_Int32 nFound = aMethodName.lastIndexOf( '_', nSearchFrom ); + if( nFound == -1 ) + break; + + OUString aPureMethodName = aMethodName.copy( nFound + 1 ); + + aIt = maMethodNameMap.find( aPureMethodName ); + if (aIt != maMethodNameMap.end()) + { + // Check if it can be a type? + // Problem: Does not work if package names contain _ ?! + OUString aStr = aMethodName.copy( 0, nFound ); + OUString aTypeName = aStr.replace( '_', '.' ); + Reference< XIdlClass > xClass = mxCoreReflection->forName( aTypeName ); + if( xClass.is() ) + { + // If this is a valid class it could be the right method + + // Could be the right method, type has to be checked + const sal_Int32 iHashResult = aIt->second; + + const Reference<XIdlMethod> xMethod = maAllMethodSeq[iHashResult]; + + Reference< XIdlClass > xMethClass = xMethod->getDeclaringClass(); + if( xClass->equals( xMethClass ) ) + { + return iHashResult; + } + else + { + // Could also be another method with the same name + // Iterate over all methods + size_t nLen = maAllMethodSeq.size(); + for (size_t i = 0; i < nLen; ++i) + { + const Reference<XIdlMethod> xMethod2 = maAllMethodSeq[ i ]; + if( xMethod2->getName() == aPureMethodName ) + { + Reference< XIdlClass > xMethClass2 = xMethod2->getDeclaringClass(); + + if( xClass->equals( xMethClass2 ) ) + { + return i; + } + } + } + } + } + } + + nSearchFrom = nFound - 1; + if( nSearchFrom < 0 ) + break; + } + return -1; +} + +void IntrospectionAccessStatic_Impl::setPropertyValue( const Any& obj, const OUString& aPropertyName, const Any& aValue ) const +//void IntrospectionAccessStatic_Impl::setPropertyValue( Any& obj, const OUString& aPropertyName, const Any& aValue ) const +{ + sal_Int32 i = getPropertyIndex( aPropertyName ); + if( i == -1 ) + throw UnknownPropertyException(aPropertyName); + setPropertyValueByIndex( obj, i, aValue ); +} + +void IntrospectionAccessStatic_Impl::setPropertyValueByIndex(const Any& obj, sal_Int32 nSequenceIndex, const Any& aValue) const +//void IntrospectionAccessStatic_Impl::setPropertyValueByIndex( Any& obj, sal_Int32 nSequenceIndex, const Any& aValue) const +{ + // Is the passed object something that fits? + Reference<XInterface> xInterface; + if( !(obj >>= xInterface) ) + { + TypeClass eObjType = obj.getValueType().getTypeClass(); + if( nSequenceIndex >= mnPropCount) + throw IllegalArgumentException( + "IntrospectionAccessStatic_Impl::setPropertyValueByIndex(), index > propertyCount, " + + OUString::number(nSequenceIndex) + " > " + OUString::number(mnPropCount), + Reference<XInterface>(), 0); + if( eObjType != TypeClass_STRUCT && eObjType != TypeClass_EXCEPTION ) + throw IllegalArgumentException( + "IntrospectionAccessStatic_Impl::setPropertyValueByIndex(), expected struct or exception, got" + + obj.getValueType().getTypeName(), Reference<XInterface>(), 0); + } + + // Test flags + if( (maAllPropertySeq[ nSequenceIndex ].Attributes & READONLY) != 0 ) + { + throw UnknownPropertyException( + "IntrospectionAccessStatic_Impl::setPropertyValueByIndex(), property at index " + OUString::number(nSequenceIndex) + " is readonly"); + } + + switch( maMapTypeSeq[ nSequenceIndex ] ) + { + case MAP_PROPERTY_SET: + { + // Get Property + const Property& rProp = maAllPropertySeq[ nSequenceIndex ]; + + // Convert Interface-Parameter to the correct type + bool bUseCopy = false; + Any aRealValue; + + if( auto valInterface = o3tl::tryAccess< + css::uno::Reference<css::uno::XInterface>>(aValue) ) + { + Type aPropType = rProp.Type; + OUString aTypeName( aPropType.getTypeName() ); + Reference< XIdlClass > xPropClass = mxCoreReflection->forName( aTypeName ); + //Reference<XIdlClass> xPropClass = rProp.Type; + if( xPropClass.is() && xPropClass->getTypeClass() == TypeClass_INTERFACE ) + { + if( valInterface->is() ) + { + //Any queryInterface( const Type& rType ); + aRealValue = (*valInterface)->queryInterface( aPropType ); + if( aRealValue.hasValue() ) + bUseCopy = true; + } + } + } + + // Do we have a FastPropertySet and a valid Handle? + // CAUTION: At this point we exploit that the PropertySet + // gets queried at the beginning of the Introspection-Process. + sal_Int32 nOrgHandle; + if( mbFastPropSet && ( nOrgHandle = mpOrgPropertyHandleArray[ nSequenceIndex ] ) != -1 ) + { + // Retrieve PropertySet-Interface + Reference<XFastPropertySet> xFastPropSet = + Reference<XFastPropertySet>::query( xInterface ); + if( xFastPropSet.is() ) + { + xFastPropSet->setFastPropertyValue( nOrgHandle, bUseCopy ? aRealValue : aValue ); + } + else + { + // throw UnknownPropertyException + } + } + // else take the normal one + else + { + // Retrieve PropertySet-Interface + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( xInterface ); + if( xPropSet.is() ) + { + xPropSet->setPropertyValue( rProp.Name, bUseCopy ? aRealValue : aValue ); + } + else + { + // throw UnknownPropertyException + } + } + } + break; + + case MAP_FIELD: + { + Reference<XIdlField> xField = static_cast<XIdlField*>(aInterfaceSeq1[ nSequenceIndex ].get()); + Reference<XIdlField2> xField2(xField, UNO_QUERY); + if( xField2.is() ) + { + xField2->set( const_cast<Any&>(obj), aValue ); + // IllegalArgumentException + // NullPointerException + } else + if( xField.is() ) + { + xField->set( obj, aValue ); + // IllegalArgumentException + // NullPointerException + } + else + { + // throw IllegalArgumentException(); + } + } + break; + + case MAP_GETSET: + case MAP_SETONLY: + { + // Fetch set method + Reference<XIdlMethod> xMethod = static_cast<XIdlMethod*>(aInterfaceSeq2[ nSequenceIndex ].get()); + if( xMethod.is() ) + { + Sequence<Any> args( 1 ); + args.getArray()[0] = aValue; + xMethod->invoke( obj, args ); + } + else + { + // throw IllegalArgumentException(); + } + } + break; + } +} + +Any IntrospectionAccessStatic_Impl::getPropertyValue( const Any& obj, const OUString& aPropertyName ) const +{ + sal_Int32 i = getPropertyIndex( aPropertyName ); + if( i != -1 ) + return getPropertyValueByIndex( obj, i ); + + throw UnknownPropertyException(aPropertyName); +} + +Any IntrospectionAccessStatic_Impl::getPropertyValueByIndex(const Any& obj, sal_Int32 nSequenceIndex) const +{ + Any aRet; + + // Is there anything suitable in the passed object? + Reference<XInterface> xInterface; + if( !(obj >>= xInterface) ) + { + TypeClass eObjType = obj.getValueType().getTypeClass(); + if( nSequenceIndex >= mnPropCount || ( eObjType != TypeClass_STRUCT && eObjType != TypeClass_EXCEPTION ) ) + { + // throw IllegalArgumentException(); + return aRet; + } + } + + switch( maMapTypeSeq[ nSequenceIndex ] ) + { + case MAP_PROPERTY_SET: + { + // Acquire property + const Property& rProp = maAllPropertySeq[ nSequenceIndex ]; + + // Do we have a FastPropertySet and a valid handle? + // NOTE: At this point is exploited that the PropertySet + // is queried at the beginning of introspection process. + sal_Int32 nOrgHandle; + if( mbFastPropSet && ( nOrgHandle = mpOrgPropertyHandleArray[ nSequenceIndex ] ) != -1 ) + { + // Fetch the PropertySet interface + Reference<XFastPropertySet> xFastPropSet = + Reference<XFastPropertySet>::query( xInterface ); + if( xFastPropSet.is() ) + { + aRet = xFastPropSet->getFastPropertyValue( nOrgHandle); + } + else + { + // throw UnknownPropertyException + return aRet; + } + } + // Otherwise use the normal one + else + { + // Fetch the PropertySet interface + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( xInterface ); + if( xPropSet.is() ) + { + aRet = xPropSet->getPropertyValue( rProp.Name ); + } + else + { + // throw UnknownPropertyException + return aRet; + } + } + } + break; + + case MAP_FIELD: + { + Reference<XIdlField> xField = static_cast<XIdlField*>(aInterfaceSeq1[ nSequenceIndex ].get()); + if( xField.is() ) + { + aRet = xField->get( obj ); + // IllegalArgumentException + // NullPointerException + } + else + { + // throw IllegalArgumentException(); + return aRet; + } + } + break; + + case MAP_GETSET: + { + // Fetch get method + Reference<XIdlMethod> xMethod = static_cast<XIdlMethod*>(aInterfaceSeq1[ nSequenceIndex ].get()); + if( xMethod.is() ) + { + Sequence<Any> args; + aRet = xMethod->invoke( obj, args ); + } + else + { + // throw IllegalArgumentException(); + return aRet; + } + } + break; + + case MAP_SETONLY: + // Get method does not exist + // throw WriteOnlyPropertyException(); + return aRet; + } + return aRet; +} + + +// Helper method to adjust the size of the vectors +void IntrospectionAccessStatic_Impl::checkPropertyArraysSize( sal_Int32 iNextIndex ) +{ + sal_Int32 nLen = static_cast<sal_Int32>(maAllPropertySeq.size()); + if( iNextIndex >= nLen ) + { + maAllPropertySeq.resize( nLen + ARRAY_SIZE_STEP ); + maMapTypeSeq.resize( nLen + ARRAY_SIZE_STEP ); + maPropertyConceptSeq.resize( nLen + ARRAY_SIZE_STEP ); + } +} + +void IntrospectionAccessStatic_Impl::checkInterfaceArraySize( std::vector< Reference<XInterface> >& rSeq, + std::vector<Reference<XInterface>>& rInterfaceVec, sal_Int32 iNextIndex ) +{ + sal_Int32 nLen = rSeq.size(); + if( iNextIndex >= nLen ) + { + // Synchronize new size with ARRAY_SIZE_STEP + sal_Int32 nMissingSize = iNextIndex - nLen + 1; + sal_Int32 nSteps = nMissingSize / ARRAY_SIZE_STEP + 1; + sal_Int32 nNewSize = nLen + nSteps * ARRAY_SIZE_STEP; + + rSeq.resize( nNewSize ); + rInterfaceVec = rSeq; + } +} + + +//*** ImplIntrospectionAccess *** + + +// New Impl class as part of the introspection conversion to instance-bound +// Introspection with property access via XPropertySet. The old class +// ImplIntrospectionAccess lives on as IntrospectionAccessStatic_Impl +class ImplIntrospectionAccess : public IntrospectionAccessHelper +{ + friend class Implementation; + + // Object under examination + Any maInspectedObject; + + // As interface + Reference<XInterface> mxIface; + + // Static introspection data + rtl::Reference< IntrospectionAccessStatic_Impl > mpStaticImpl; + + // Last Sequence that came with getProperties (optimization) + Sequence<Property> maLastPropertySeq; + sal_Int32 mnLastPropertyConcept; + + // Last Sequence that came with getMethods (optimization) + Sequence<Reference<XIdlMethod> > maLastMethodSeq; + sal_Int32 mnLastMethodConcept; + + // Guards the caching of queried interfaces + std::mutex m_aMutex; + + // Original interfaces of the objects + Reference<XElementAccess> mxObjElementAccess; + Reference<XNameContainer> mxObjNameContainer; + Reference<XNameReplace> mxObjNameReplace; + Reference<XNameAccess> mxObjNameAccess; + Reference<XIndexContainer> mxObjIndexContainer; + Reference<XIndexReplace> mxObjIndexReplace; + Reference<XIndexAccess> mxObjIndexAccess; + Reference<XEnumerationAccess> mxObjEnumerationAccess; + Reference<XIdlArray> mxObjIdlArray; + + Reference<XElementAccess> getXElementAccess(); + Reference<XNameContainer> getXNameContainer(); + Reference<XNameReplace> getXNameReplace(); + Reference<XNameAccess> getXNameAccess(); + Reference<XIndexContainer> getXIndexContainer(); + Reference<XIndexReplace> getXIndexReplace(); + Reference<XIndexAccess> getXIndexAccess(); + Reference<XEnumerationAccess> getXEnumerationAccess(); + Reference<XIdlArray> getXIdlArray(); + + void cacheXNameContainer(); + void cacheXIndexContainer(); + +public: + ImplIntrospectionAccess( Any obj, rtl::Reference< IntrospectionAccessStatic_Impl > pStaticImpl_ ); + + // Methods from XIntrospectionAccess + virtual sal_Int32 SAL_CALL getSuppliedMethodConcepts() override; + virtual sal_Int32 SAL_CALL getSuppliedPropertyConcepts() override; + virtual Property SAL_CALL getProperty(const OUString& Name, sal_Int32 PropertyConcepts) override; + virtual sal_Bool SAL_CALL hasProperty(const OUString& Name, sal_Int32 PropertyConcepts) override; + virtual Sequence< Property > SAL_CALL getProperties(sal_Int32 PropertyConcepts) override; + virtual Reference<XIdlMethod> SAL_CALL getMethod(const OUString& Name, sal_Int32 MethodConcepts) override; + virtual sal_Bool SAL_CALL hasMethod(const OUString& Name, sal_Int32 MethodConcepts) override; + virtual Sequence< Reference<XIdlMethod> > SAL_CALL getMethods(sal_Int32 MethodConcepts) override; + virtual Sequence< Type > SAL_CALL getSupportedListeners() override; + using OWeakObject::queryAdapter; + virtual Reference<XInterface> SAL_CALL queryAdapter( const Type& rType ) override; + + // Methods from XMaterialHolder + virtual Any SAL_CALL getMaterial() override; + + // Methods from XExactName + virtual OUString SAL_CALL getExactName( const OUString& rApproximateName ) override; + + // Methods from XInterface + virtual Any SAL_CALL queryInterface( const Type& rType ) override; + virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override { OWeakObject::release(); } + + // Methods from XPropertySet + virtual Reference<XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue(const OUString& aPropertyName, const Any& aValue) override; + virtual Any SAL_CALL getPropertyValue(const OUString& aPropertyName) override; + virtual void SAL_CALL addPropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) override; + virtual void SAL_CALL removePropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) override; + virtual void SAL_CALL addVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) override; + virtual void SAL_CALL removeVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) override; + + // Methods from XFastPropertySet + virtual void SAL_CALL setFastPropertyValue(sal_Int32 nHandle, const Any& aValue) override; + virtual Any SAL_CALL getFastPropertyValue(sal_Int32 nHandle) override; + + // Methods from XPropertySetInfo + virtual Sequence< Property > SAL_CALL getProperties() override; + virtual Property SAL_CALL getPropertyByName(const OUString& Name) override; + virtual sal_Bool SAL_CALL hasPropertyByName(const OUString& Name) override; + + // Methods from XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods from XNameAccess + virtual Any SAL_CALL getByName(const OUString& Name) override; + virtual Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName(const OUString& Name) override; + + // Methods from XNameReplace + virtual void SAL_CALL replaceByName(const OUString& Name, const Any& Element) override; + + // Methods from XNameContainer + virtual void SAL_CALL insertByName(const OUString& Name, const Any& Element) override; + virtual void SAL_CALL removeByName(const OUString& Name) override; + + // Methods from XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override; + virtual Any SAL_CALL getByIndex(sal_Int32 Index) override; + + // Methods from XIndexReplace + virtual void SAL_CALL replaceByIndex(sal_Int32 Index, const Any& Element) override; + + // Methods from XIndexContainer + virtual void SAL_CALL insertByIndex(sal_Int32 Index, const Any& Element) override; + virtual void SAL_CALL removeByIndex(sal_Int32 Index) override; + + // Methods from XEnumerationAccess + virtual Reference<XEnumeration> SAL_CALL createEnumeration() override; + + // Methods from XIdlArray + virtual void SAL_CALL realloc(Any& array, sal_Int32 length) override; + virtual sal_Int32 SAL_CALL getLen(const Any& array) override; + virtual Any SAL_CALL get(const Any& array, sal_Int32 index) override; + virtual void SAL_CALL set(Any& array, sal_Int32 index, const Any& value) override; + + // Methods from XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const Sequence< sal_Int8 >& aIdentifier ) override; +}; + +ImplIntrospectionAccess::ImplIntrospectionAccess + ( Any obj, rtl::Reference< IntrospectionAccessStatic_Impl > pStaticImpl_ ) + : maInspectedObject(std::move( obj )), mpStaticImpl(std::move( pStaticImpl_ )) , + mnLastPropertyConcept(-1), mnLastMethodConcept(-1) //, maAdapter() +{ + // Save object as an interface if possible + maInspectedObject >>= mxIface; +} + +Reference<XElementAccess> ImplIntrospectionAccess::getXElementAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjElementAccess.is() ) + { + aGuard.unlock(); + Reference<XElementAccess> xElementAccess( mxIface, UNO_QUERY ); + aGuard.lock(); + if( !mxObjElementAccess.is() ) + mxObjElementAccess = xElementAccess; + } + return mxObjElementAccess; +} + +void ImplIntrospectionAccess::cacheXNameContainer() +{ + Reference<XNameContainer> xNameContainer; + Reference<XNameReplace> xNameReplace; + Reference<XNameAccess> xNameAccess; + if (mpStaticImpl->mbNameContainer) + { + xNameContainer.set( mxIface, UNO_QUERY ); + xNameReplace = xNameContainer; + xNameAccess = xNameContainer; + } + else if (mpStaticImpl->mbNameReplace) + { + xNameReplace.set( mxIface, UNO_QUERY ); + xNameAccess = xNameReplace; + } + else if (mpStaticImpl->mbNameAccess) + { + xNameAccess.set( mxIface, UNO_QUERY ); + } + + { + std::unique_lock aGuard( m_aMutex ); + if( !mxObjNameContainer.is() ) + mxObjNameContainer = xNameContainer; + if( !mxObjNameReplace.is() ) + mxObjNameReplace = xNameReplace; + if( !mxObjNameAccess.is() ) + mxObjNameAccess = xNameAccess; + } +} + +Reference<XNameContainer> ImplIntrospectionAccess::getXNameContainer() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjNameContainer.is() ) + { + aGuard.unlock(); + cacheXNameContainer(); + } + return mxObjNameContainer; +} + +Reference<XNameReplace> ImplIntrospectionAccess::getXNameReplace() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjNameReplace.is() ) + { + aGuard.unlock(); + cacheXNameContainer(); + } + return mxObjNameReplace; +} + +Reference<XNameAccess> ImplIntrospectionAccess::getXNameAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjNameAccess.is() ) + { + aGuard.unlock(); + cacheXNameContainer(); + } + return mxObjNameAccess; +} + +void ImplIntrospectionAccess::cacheXIndexContainer() +{ + Reference<XIndexContainer> xIndexContainer; + Reference<XIndexReplace> xIndexReplace; + Reference<XIndexAccess> xIndexAccess; + if (mpStaticImpl->mbIndexContainer) + { + xIndexContainer.set( mxIface, UNO_QUERY ); + xIndexReplace = xIndexContainer; + xIndexAccess = xIndexContainer; + } + else if (mpStaticImpl->mbIndexReplace) + { + xIndexReplace.set( mxIface, UNO_QUERY ); + xIndexAccess = xIndexReplace; + } + else if (mpStaticImpl->mbIndexAccess) + { + xIndexAccess.set( mxIface, UNO_QUERY ); + } + + { + std::unique_lock aGuard( m_aMutex ); + if( !mxObjIndexContainer.is() ) + mxObjIndexContainer = xIndexContainer; + if( !mxObjIndexReplace.is() ) + mxObjIndexReplace = xIndexReplace; + if( !mxObjIndexAccess.is() ) + mxObjIndexAccess = xIndexAccess; + } +} + +Reference<XIndexContainer> ImplIntrospectionAccess::getXIndexContainer() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIndexContainer.is() ) + { + aGuard.unlock(); + cacheXIndexContainer(); + } + return mxObjIndexContainer; +} + +Reference<XIndexReplace> ImplIntrospectionAccess::getXIndexReplace() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIndexReplace.is() ) + { + aGuard.unlock(); + cacheXIndexContainer(); + } + return mxObjIndexReplace; +} + +Reference<XIndexAccess> ImplIntrospectionAccess::getXIndexAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIndexAccess.is() ) + { + aGuard.unlock(); + cacheXIndexContainer(); + } + return mxObjIndexAccess; +} + +Reference<XEnumerationAccess> ImplIntrospectionAccess::getXEnumerationAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjEnumerationAccess.is() ) + { + aGuard.unlock(); + Reference<XEnumerationAccess> xEnumerationAccess( mxIface, UNO_QUERY ); + aGuard.lock(); + if( !mxObjEnumerationAccess.is() ) + mxObjEnumerationAccess = xEnumerationAccess; + } + return mxObjEnumerationAccess; +} + +Reference<XIdlArray> ImplIntrospectionAccess::getXIdlArray() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIdlArray.is() ) + { + aGuard.unlock(); + Reference<XIdlArray> xIdlArray( mxIface, UNO_QUERY ); + aGuard.lock(); + if( !mxObjIdlArray.is() ) + mxObjIdlArray = xIdlArray; + } + return mxObjIdlArray; +} + +// Methods from XInterface +Any SAL_CALL ImplIntrospectionAccess::queryInterface( const Type& rType ) +{ + Any aRet( ::cppu::queryInterface( + rType, + static_cast< XIntrospectionAccess * >( this ), + static_cast< XMaterialHolder * >( this ), + static_cast< XExactName * >( this ), + static_cast< XPropertySet * >( this ), + static_cast< XFastPropertySet * >( this ), + static_cast< XPropertySetInfo * >( this ) ) ); + if( !aRet.hasValue() ) + aRet = OWeakObject::queryInterface( rType ); + + if( !aRet.hasValue() ) + { + // Wrapper for the object interfaces + ( mpStaticImpl->mbElementAccess && (aRet = ::cppu::queryInterface + ( rType, static_cast< XElementAccess* >( static_cast< XNameAccess* >( this ) ) ) ).hasValue() ) + || ( mpStaticImpl->mbNameAccess && (aRet = ::cppu::queryInterface( rType, static_cast< XNameAccess* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbNameReplace && (aRet = ::cppu::queryInterface( rType, static_cast< XNameReplace* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbNameContainer && (aRet = ::cppu::queryInterface( rType, static_cast< XNameContainer* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIndexAccess && (aRet = ::cppu::queryInterface( rType, static_cast< XIndexAccess* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIndexReplace && (aRet = ::cppu::queryInterface( rType, static_cast< XIndexReplace* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIndexContainer && (aRet = ::cppu::queryInterface( rType, static_cast< XIndexContainer* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbEnumerationAccess && (aRet = ::cppu::queryInterface( rType, static_cast< XEnumerationAccess* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIdlArray && (aRet = ::cppu::queryInterface( rType, static_cast< XIdlArray* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbUnoTunnel && (aRet = ::cppu::queryInterface( rType, static_cast< XUnoTunnel* >( this ) ) ).hasValue() ); + } + return aRet; +} + + +//*** Implementation of ImplIntrospectionAdapter *** + + +// Methods from XPropertySet +Reference<XPropertySetInfo> ImplIntrospectionAccess::getPropertySetInfo() +{ + return static_cast<XPropertySetInfo *>(this); +} + +void ImplIntrospectionAccess::setPropertyValue(const OUString& aPropertyName, const Any& aValue) +{ + mpStaticImpl->setPropertyValue( maInspectedObject, aPropertyName, aValue ); +} + +Any ImplIntrospectionAccess::getPropertyValue(const OUString& aPropertyName) +{ + return mpStaticImpl->getPropertyValue( maInspectedObject, aPropertyName ); +} + +void ImplIntrospectionAccess::addPropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + //Reference<XPropertySet> xPropSet( mxIface, USR_QUERY ); + if( xPropSet.is() ) + xPropSet->addPropertyChangeListener(aPropertyName, aListener); + } +} + +void ImplIntrospectionAccess::removePropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + //Reference<XPropertySet> xPropSet( mxIface, USR_QUERY ); + if( xPropSet.is() ) + xPropSet->removePropertyChangeListener(aPropertyName, aListener); + } +} + +void ImplIntrospectionAccess::addVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + //Reference<XPropertySet> xPropSet( mxIface, USR_QUERY ); + if( xPropSet.is() ) + xPropSet->addVetoableChangeListener(aPropertyName, aListener); + } +} + +void ImplIntrospectionAccess::removeVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + if( xPropSet.is() ) + xPropSet->removeVetoableChangeListener(aPropertyName, aListener); + } +} + + +// Methods from XFastPropertySet +void ImplIntrospectionAccess::setFastPropertyValue(sal_Int32, const Any&) +{ +} + +Any ImplIntrospectionAccess::getFastPropertyValue(sal_Int32) +{ + return Any(); +} + +// Methods from XPropertySetInfo +Sequence< Property > ImplIntrospectionAccess::getProperties() +{ + return comphelper::containerToSequence(mpStaticImpl->getProperties()); +} + +Property ImplIntrospectionAccess::getPropertyByName(const OUString& Name) +{ + return getProperty( Name, PropertyConcept::ALL ); +} + +sal_Bool ImplIntrospectionAccess::hasPropertyByName(const OUString& Name) +{ + return hasProperty( Name, PropertyConcept::ALL ); +} + +// Methods from XElementAccess +Type ImplIntrospectionAccess::getElementType() +{ + return getXElementAccess()->getElementType(); +} + +sal_Bool ImplIntrospectionAccess::hasElements() +{ + return getXElementAccess()->hasElements(); +} + +// Methods from XNameAccess +Any ImplIntrospectionAccess::getByName(const OUString& Name) +{ + return getXNameAccess()->getByName( Name ); +} + +Sequence< OUString > ImplIntrospectionAccess::getElementNames() +{ + return getXNameAccess()->getElementNames(); +} + +sal_Bool ImplIntrospectionAccess::hasByName(const OUString& Name) +{ + return getXNameAccess()->hasByName( Name ); +} + +// Methods from XNameContainer +void ImplIntrospectionAccess::insertByName(const OUString& Name, const Any& Element) +{ + getXNameContainer()->insertByName( Name, Element ); +} + +void ImplIntrospectionAccess::replaceByName(const OUString& Name, const Any& Element) +{ + getXNameReplace()->replaceByName( Name, Element ); +} + +void ImplIntrospectionAccess::removeByName(const OUString& Name) +{ + getXNameContainer()->removeByName( Name ); +} + +// Methods from XIndexAccess +// Already in XNameAccess: virtual Reference<XIdlClass> getElementType() const +sal_Int32 ImplIntrospectionAccess::getCount() +{ + return getXIndexAccess()->getCount(); +} + +Any ImplIntrospectionAccess::getByIndex(sal_Int32 Index) +{ + return getXIndexAccess()->getByIndex( Index ); +} + +// Methods from XIndexContainer +void ImplIntrospectionAccess::insertByIndex(sal_Int32 Index, const Any& Element) +{ + getXIndexContainer()->insertByIndex( Index, Element ); +} + +void ImplIntrospectionAccess::replaceByIndex(sal_Int32 Index, const Any& Element) +{ + getXIndexReplace()->replaceByIndex( Index, Element ); +} + +void ImplIntrospectionAccess::removeByIndex(sal_Int32 Index) +{ + getXIndexContainer()->removeByIndex( Index ); +} + +// Methods from XEnumerationAccess +// Already in XNameAccess: virtual Reference<XIdlClass> getElementType() const; +Reference<XEnumeration> ImplIntrospectionAccess::createEnumeration() +{ + return getXEnumerationAccess()->createEnumeration(); +} + +// Methods from XIdlArray +void ImplIntrospectionAccess::realloc(Any& array, sal_Int32 length) +{ + getXIdlArray()->realloc( array, length ); +} + +sal_Int32 ImplIntrospectionAccess::getLen(const Any& array) +{ + return getXIdlArray()->getLen( array ); +} + +Any ImplIntrospectionAccess::get(const Any& array, sal_Int32 index) +{ + return getXIdlArray()->get( array, index ); +} + +void ImplIntrospectionAccess::set(Any& array, sal_Int32 index, const Any& value) +{ + getXIdlArray()->set( array, index, value ); +} + +// Methods from XUnoTunnel +sal_Int64 ImplIntrospectionAccess::getSomething( const Sequence< sal_Int8 >& aIdentifier ) +{ + if (Reference<XUnoTunnel> xUnoTunnel{ mxIface, css::uno::UNO_QUERY }) + return xUnoTunnel->getSomething(aIdentifier); + return 0; +} + + +//*** Implementation of ImplIntrospectionAccess *** + +// Methods from XIntrospectionAccess +sal_Int32 ImplIntrospectionAccess::getSuppliedMethodConcepts() +{ + return MethodConcept::DANGEROUS | + PROPERTY | + LISTENER | + ENUMERATION | + NAMECONTAINER | + INDEXCONTAINER; +} + +sal_Int32 ImplIntrospectionAccess::getSuppliedPropertyConcepts() +{ + return PropertyConcept::DANGEROUS | + PROPERTYSET | + ATTRIBUTES | + METHODS; +} + +Property ImplIntrospectionAccess::getProperty(const OUString& Name, sal_Int32 PropertyConcepts) +{ + Property aRet; + sal_Int32 i = mpStaticImpl->getPropertyIndex( Name ); + bool bFound = false; + if( i != -1 ) + { + sal_Int32 nConcept = mpStaticImpl->getPropertyConcepts()[ i ]; + if( (PropertyConcepts & nConcept) != 0 ) + { + aRet = mpStaticImpl->getProperties()[ i ]; + bFound = true; + } + } + if( !bFound ) + throw NoSuchElementException(Name); + return aRet; +} + +sal_Bool ImplIntrospectionAccess::hasProperty(const OUString& Name, sal_Int32 PropertyConcepts) +{ + sal_Int32 i = mpStaticImpl->getPropertyIndex( Name ); + bool bRet = false; + if( i != -1 ) + { + sal_Int32 nConcept = mpStaticImpl->getPropertyConcepts()[ i ]; + if( (PropertyConcepts & nConcept) != 0 ) + bRet = true; + } + return bRet; +} + +Sequence< Property > ImplIntrospectionAccess::getProperties(sal_Int32 PropertyConcepts) +{ + // If all supported concepts are required, simply pass through the sequence + sal_Int32 nAllSupportedMask = PROPERTYSET | + ATTRIBUTES | + METHODS; + if( ( PropertyConcepts & nAllSupportedMask ) == nAllSupportedMask ) + { + return comphelper::containerToSequence(mpStaticImpl->getProperties()); + } + + // Same sequence as last time? + if( mnLastPropertyConcept == PropertyConcepts ) + { + return maLastPropertySeq; + } + + // Number of properties to be delivered + sal_Int32 nCount = 0; + + // There are currently no DANGEROUS properties + // if( PropertyConcepts & DANGEROUS ) + // nCount += mpStaticImpl->mnDangerousPropCount; + if( PropertyConcepts & PROPERTYSET ) + nCount += mpStaticImpl->mnPropertySetPropCount; + if( PropertyConcepts & ATTRIBUTES ) + nCount += mpStaticImpl->mnAttributePropCount; + if( PropertyConcepts & METHODS ) + nCount += mpStaticImpl->mnMethodPropCount; + + // Realloc sequence according to the required number + maLastPropertySeq.realloc( nCount ); + Property* pDestProps = maLastPropertySeq.getArray(); + + // Go through all the properties and apply according to the concept + const std::vector<Property>& rPropSeq = mpStaticImpl->getProperties(); + const std::vector<sal_Int32>& rConcepts = mpStaticImpl->getPropertyConcepts(); + sal_Int32 nLen = static_cast<sal_Int32>(rPropSeq.size()); + + sal_Int32 iDest = 0; + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + sal_Int32 nConcept = rConcepts[ i ]; + if( nConcept & PropertyConcepts ) + pDestProps[ iDest++ ] = rPropSeq[ i ]; + } + + // Remember PropertyConcept representing maLastPropertySeq + mnLastPropertyConcept = PropertyConcepts; + + // Supply assembled Sequence + return maLastPropertySeq; +} + +Reference<XIdlMethod> ImplIntrospectionAccess::getMethod(const OUString& Name, sal_Int32 MethodConcepts) +{ + Reference<XIdlMethod> xRet; + sal_Int32 i = mpStaticImpl->getMethodIndex( Name ); + if( i != -1 ) + { + + sal_Int32 nConcept = mpStaticImpl->getMethodConcepts()[ i ]; + if( (MethodConcepts & nConcept) != 0 ) + { + xRet = mpStaticImpl->getMethods()[i]; + } + } + if( !xRet.is() ) + throw NoSuchMethodException(Name); + return xRet; +} + +sal_Bool ImplIntrospectionAccess::hasMethod(const OUString& Name, sal_Int32 MethodConcepts) +{ + sal_Int32 i = mpStaticImpl->getMethodIndex( Name ); + bool bRet = false; + if( i != -1 ) + { + sal_Int32 nConcept = mpStaticImpl->getMethodConcepts()[ i ]; + if( (MethodConcepts & nConcept) != 0 ) + bRet = true; + } + return bRet; +} + +Sequence< Reference<XIdlMethod> > ImplIntrospectionAccess::getMethods(sal_Int32 MethodConcepts) +{ + // If all supported concepts are required, simply pass through the sequence + sal_Int32 nAllSupportedMask = MethodConcept::DANGEROUS | + PROPERTY | + LISTENER | + ENUMERATION | + NAMECONTAINER | + INDEXCONTAINER | + MethodConcept_NORMAL_IMPL; + if( ( MethodConcepts & nAllSupportedMask ) == nAllSupportedMask ) + { + return comphelper::containerToSequence(mpStaticImpl->getMethods()); + } + + // Same sequence as last time? + if( mnLastMethodConcept == MethodConcepts ) + { + return maLastMethodSeq; + } + + // Get method sequences + const std::vector< Reference<XIdlMethod> >& aMethodSeq = mpStaticImpl->getMethods(); + sal_Int32 nLen = static_cast<sal_Int32>(aMethodSeq.size()); + + // Realloc sequence according to the required number + // Unlike Properties, the number can not be determined by counters in + // inspect() beforehand, since methods can belong to several concepts + maLastMethodSeq.realloc( nLen ); + Reference<XIdlMethod>* pDestMethods = maLastMethodSeq.getArray(); + + // Go through all the methods and apply according to the concept + sal_Int32 iDest = 0; + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + sal_Int32 nConcept = mpStaticImpl->getMethodConcepts()[ i ]; + if( nConcept & MethodConcepts ) + pDestMethods[ iDest++ ] = aMethodSeq[ i ]; + } + + // Bring to the correct length + maLastMethodSeq.realloc( iDest ); + + // Remember MethodConcept representing maLastMethodSeq + mnLastMethodConcept = MethodConcepts; + + // Supply assembled Sequence + return maLastMethodSeq; +} + +Sequence< Type > ImplIntrospectionAccess::getSupportedListeners() +{ + return comphelper::containerToSequence(mpStaticImpl->getSupportedListeners()); +} + +Reference<XInterface> SAL_CALL ImplIntrospectionAccess::queryAdapter( const Type& rType ) +{ + Reference<XInterface> xRet; + if( rType == cppu::UnoType<XInterface>::get() + || rType == cppu::UnoType<XPropertySet>::get() + || rType == cppu::UnoType<XFastPropertySet>::get() + || rType == cppu::UnoType<XPropertySetInfo>::get() + || rType == cppu::UnoType<XElementAccess>::get() + || rType == cppu::UnoType<XNameAccess>::get() + || rType == cppu::UnoType<XNameReplace>::get() + || rType == cppu::UnoType<XNameContainer>::get() + || rType == cppu::UnoType<XIndexAccess>::get() + || rType == cppu::UnoType<XIndexReplace>::get() + || rType == cppu::UnoType<XIndexContainer>::get() + || rType == cppu::UnoType<XEnumerationAccess>::get() + || rType == cppu::UnoType<XIdlArray>::get() + || rType == cppu::UnoType<XUnoTunnel>::get() ) + { + queryInterface( rType ) >>= xRet; + } + return xRet; +} + +// Methods from XMaterialHolder +Any ImplIntrospectionAccess::getMaterial() +{ + return maInspectedObject; +} + +// Methods from XExactName +OUString ImplIntrospectionAccess::getExactName( const OUString& rApproximateName ) +{ + OUString aRetStr; + LowerToExactNameMap::iterator aIt = + mpStaticImpl->maLowerToExactNameMap.find( rApproximateName.toAsciiLowerCase() ); + if (aIt != mpStaticImpl->maLowerToExactNameMap.end()) + aRetStr = (*aIt).second; + return aRetStr; +} + +struct TypeKey { + TypeKey( + css::uno::Reference<css::beans::XPropertySetInfo> theProperties, + std::vector<css::uno::Type> const & theTypes): + properties(std::move(theProperties)) + { + //TODO: Could even sort the types lexicographically first, to increase + // the chance of matches between different implementations' getTypes(), + // but the old scheme of using getImplementationId() would have missed + // those matches, too: + OUStringBuffer b(static_cast<int>(theTypes.size() * 64)); + for (const css::uno::Type& rType : theTypes) { + b.append(rType.getTypeName() + + "*"); // arbitrary delimiter not used by type grammar + } + types = b.makeStringAndClear(); + } + + css::uno::Reference<css::beans::XPropertySetInfo> properties; + OUString types; +}; + +struct TypeKeyLess { + bool operator ()(TypeKey const & key1, TypeKey const & key2) const { + if (key1.properties.get() < key2.properties.get()) { + return true; + } + if (key1.properties.get() > key2.properties.get()) { + return false; + } + return key1.types < key2.types; + } +}; + +template<typename Key, typename Less> class Cache { +public: + rtl::Reference<IntrospectionAccessStatic_Impl> find(Key const & key) const { + typename Map::const_iterator i(map_.find(key)); + if (i == map_.end()) { + return rtl::Reference<IntrospectionAccessStatic_Impl>(); + } else { + if (i->second.hits < std::numeric_limits<unsigned>::max()) { + ++i->second.hits; + } + assert(i->second.access.is()); + return i->second.access; + } + } + + void insert( + Key const & key, + rtl::Reference<IntrospectionAccessStatic_Impl> const & access) + { + assert(access.is()); + typename Map::size_type const MAX = 100; + assert(map_.size() <= MAX); + if (map_.size() == MAX) { + typename Map::iterator del = std::min_element(map_.begin(), map_.end(), + [](const typename Map::value_type& a, const typename Map::value_type& b) { + return a.second.hits < b.second.hits; + }); + map_.erase(del); + } + bool ins = map_.emplace(key, Data(access)).second; + assert(ins); (void)ins; + } + + void clear() { map_.clear(); } + +private: + struct Data { + explicit Data( + rtl::Reference<IntrospectionAccessStatic_Impl> theAccess): + access(std::move(theAccess)), hits(1) + {} + + rtl::Reference<IntrospectionAccessStatic_Impl> access; + mutable unsigned hits; + }; + + typedef std::map<Key, Data, Less> Map; + + Map map_; +}; + +typedef + cppu::WeakComponentImplHelper< + css::lang::XServiceInfo, css::beans::XIntrospection> + Implementation_Base; + +class Implementation: private cppu::BaseMutex, public Implementation_Base { +public: + explicit Implementation( + css::uno::Reference<css::uno::XComponentContext> const & context): + Implementation_Base(m_aMutex), + reflection_(css::reflection::theCoreReflection::get(context)) + {} + +private: + virtual void SAL_CALL disposing() override { + osl::MutexGuard g(m_aMutex); + reflection_.clear(); + typeCache_.clear(); + } + + virtual OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.stoc.Introspection"; } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return cppu::supportsService(this, ServiceName); } + + virtual css::uno::Sequence<OUString> SAL_CALL + getSupportedServiceNames() override + { + Sequence<OUString> s { "com.sun.star.beans.Introspection" }; + return s; + } + + virtual css::uno::Reference<css::beans::XIntrospectionAccess> SAL_CALL + inspect(css::uno::Any const & aObject) override; + + css::uno::Reference<css::reflection::XIdlReflection> reflection_; + Cache<TypeKey, TypeKeyLess> typeCache_; +}; + +css::uno::Reference<css::beans::XIntrospectionAccess> Implementation::inspect( + css::uno::Any const & aObject) +{ + css::uno::Reference<css::reflection::XIdlReflection> reflection; + { + osl::MutexGuard g(m_aMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) { + throw css::lang::DisposedException( + getImplementationName(), getXWeak()); + } + reflection = reflection_; + } + css::uno::Any aToInspectObj; + css::uno::Type t; + if (aObject >>= t) { + css::uno::Reference<css::reflection::XIdlClass> c( + reflection->forName(t.getTypeName())); + if (!c.is()) { + SAL_WARN("stoc", "cannot reflect type " << t.getTypeName()); + return css::uno::Reference<css::beans::XIntrospectionAccess>(); + } + aToInspectObj <<= c; + } else { + aToInspectObj = aObject; + } + + // Examine object + TypeClass eType = aToInspectObj.getValueType().getTypeClass(); + if( eType != TypeClass_INTERFACE && eType != TypeClass_STRUCT && eType != TypeClass_EXCEPTION ) + return css::uno::Reference<css::beans::XIntrospectionAccess>(); + + if( auto x = o3tl::tryAccess<Reference<XInterface>>(aToInspectObj) ) + { + if( !x->is() ) + return css::uno::Reference<css::beans::XIntrospectionAccess>(); + } + + // Pointer to possibly needed new IntrospectionAccessStatic_Impl instance + rtl::Reference< IntrospectionAccessStatic_Impl > pAccess; + + // Check: Is a matching access object already cached? + std::vector< Reference<XIdlClass> > SupportedClassSeq; + std::vector< Type > SupportedTypesSeq; + Reference<XTypeProvider> xTypeProvider; + Reference<XPropertySetInfo> xPropSetInfo; + Reference<XPropertySet> xPropSet; + + // Look for interfaces XTypeProvider and PropertySet + if( eType == TypeClass_INTERFACE ) + { + xTypeProvider.set( aToInspectObj, UNO_QUERY ); + if( xTypeProvider.is() ) + { + SupportedTypesSeq = comphelper::sequenceToContainer<std::vector<Type>>(xTypeProvider->getTypes()); + } else { + SAL_WARN( + "stoc", + "object of type \"" << aToInspectObj.getValueTypeName() + << "\" lacks XTypeProvider"); + SupportedTypesSeq = { aToInspectObj.getValueType() }; + } + // Now try to get the PropertySetInfo + xPropSet.set( aToInspectObj, UNO_QUERY ); + if( xPropSet.is() ) + xPropSetInfo = xPropSet->getPropertySetInfo(); + + } else { + SupportedTypesSeq = { aToInspectObj.getValueType() }; + } + + { + osl::MutexGuard g(m_aMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) { + throw css::lang::DisposedException( + getImplementationName(), getXWeak()); + } + TypeKey key(xPropSetInfo, SupportedTypesSeq); + pAccess = typeCache_.find(key); + if (pAccess.is()) { + return new ImplIntrospectionAccess(aToInspectObj, pAccess); + } + pAccess = new IntrospectionAccessStatic_Impl(reflection); + typeCache_.insert(key, pAccess); + } + + // No access cached -> create new + std::vector<Property>& rAllPropArray = pAccess->maAllPropertySeq; + std::vector<Reference<XInterface>>& rInterfaces1 = pAccess->aInterfaceSeq1; + std::vector<Reference<XInterface>>& rInterfaces2 = pAccess->aInterfaceSeq2; + std::vector<sal_Int16>& rMapTypeArray = pAccess->maMapTypeSeq; + std::vector<sal_Int32>& rPropertyConceptArray = pAccess->maPropertyConceptSeq; + + // References to important data from pAccess + sal_Int32& rPropCount = pAccess->mnPropCount; + IntrospectionNameMap& rPropNameMap = pAccess->maPropertyNameMap; + IntrospectionNameMap& rMethodNameMap = pAccess->maMethodNameMap; + LowerToExactNameMap& rLowerToExactNameMap = pAccess->maLowerToExactNameMap; + + + //*** Perform analysis *** + + if( eType == TypeClass_INTERFACE ) + { + size_t nTypeCount = SupportedTypesSeq.size(); + if( nTypeCount ) + { + SupportedClassSeq.resize( nTypeCount ); + + for( size_t i = 0 ; i < nTypeCount ; i++ ) + SupportedClassSeq[i] = reflection->forName( SupportedTypesSeq[i].getTypeName() ); + } + + // First look for particular interfaces that are of particular + // importance to the introspection + + // Is XPropertySet present? + if( xPropSet.is() && xPropSetInfo.is() ) + { + // Is there also a FastPropertySet? + Reference<XFastPropertySet> xDummy( aToInspectObj, UNO_QUERY ); + bool bFast = pAccess->mbFastPropSet = xDummy.is(); + + Sequence<Property> aPropSeq = xPropSetInfo->getProperties(); + const Property* pProps = aPropSeq.getConstArray(); + sal_Int32 nLen = aPropSeq.getLength(); + + // For a FastPropertySet we must remember the original handles + if( bFast ) + pAccess->mpOrgPropertyHandleArray.reset( new sal_Int32[ nLen ] ); + + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + // Put property in its own list + pAccess->checkPropertyArraysSize( rPropCount ); + Property& rProp = rAllPropArray[ rPropCount ]; + rProp = pProps[ i ]; + + if( bFast ) + pAccess->mpOrgPropertyHandleArray[ i ] = rProp.Handle; + + // Enter PropCount as a handle for its own FastPropertySet + rProp.Handle = rPropCount; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_PROPERTY_SET; + rPropertyConceptArray[ rPropCount ] = PROPERTYSET; + pAccess->mnPropertySetPropCount++; + + // Enter name in hash table if not already known + OUString aPropName = rProp.Name; + + // Do we already have the name? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if( aIt == rPropNameMap.end() ) + { + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + } + else + { + SAL_WARN( "stoc", "Introspection: Property \"" << + aPropName << "\" found more than once in PropertySet" ); + } + + // Adjust count + rPropCount++; + } + } + + // Indices in the export table + sal_Int32 iAllExportedMethod = 0; + sal_Int32 iAllSupportedListener = 0; + + std::set<OUString> seen; + + // Flag, whether XInterface methods should be recorded + // (this must be done only once, allowed initially) + bool bXInterfaceIsInvalid = false; + + // Flag whether the XInterface methods have already been recorded. If + // sal_True, bXInterfaceIsInvalid is activated at the end of the interface + // loop, and XInterface methods are cut off thereafter. + bool bFoundXInterface = false; + + size_t nClassCount = SupportedClassSeq.size(); + for( size_t nIdx = 0 ; nIdx < nClassCount; nIdx++ ) + { + Reference<XIdlClass> xImplClass2 = SupportedClassSeq[nIdx]; + while( xImplClass2.is() ) + { + // Fetch interfaces from the implementation + Sequence< Reference<XIdlClass> > aClassSeq = xImplClass2->getInterfaces(); + sal_Int32 nIfaceCount = aClassSeq.getLength(); + + aClassSeq.realloc( nIfaceCount + 1 ); + aClassSeq.getArray()[ nIfaceCount ] = xImplClass2; + + for( const Reference<XIdlClass>& rxIfaceClass : std::as_const(aClassSeq) ) + { + if (!seen.insert(rxIfaceClass->getName()).second) { + continue; + } + + // 2. Register fields as properties + + // Get fields + const Sequence< Reference<XIdlField> > fields = rxIfaceClass->getFields(); + + for( const Reference<XIdlField>& xField : fields ) + { + Reference<XIdlClass> xPropType = xField->getType(); + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Enter in own property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = xField->getName(); + rProp.Handle = rPropCount; + Type aFieldType( xPropType->getTypeClass(), xPropType->getName() ); + rProp.Type = aFieldType; + FieldAccessMode eAccessMode = xField->getAccessMode(); + rProp.Attributes = (eAccessMode == FieldAccessMode_READONLY || + eAccessMode == FieldAccessMode_CONST) + ? READONLY : 0; + + // Enter name in hash table + OUString aPropName = rProp.Name; + + // Do we have the name already? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if (aIt != rPropNameMap.end()) + continue; + + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember field + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq1, + rInterfaces1, rPropCount ); + rInterfaces1[ rPropCount ] = xField; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_FIELD; + rPropertyConceptArray[ rPropCount ] = ATTRIBUTES; + pAccess->mnAttributePropCount++; + + // Adjust count + rPropCount++; + } + + + // 3. Methods + + // Get and remember all methods + Sequence< Reference<XIdlMethod> > methods = rxIfaceClass->getMethods(); + const Reference<XIdlMethod>* pSourceMethods = methods.getConstArray(); + sal_Int32 nSourceMethodCount = methods.getLength(); + + // 3. a) Search get/set and listener methods + + // Create field for information about the methods, so that methods which are not + // related to properties or listeners can easily be found later. + // New: initialise MethodConceptArray + enum MethodType + { + STANDARD_METHOD, // normal method, not related to properties or listeners + GETSET_METHOD, // belongs to a get/set property + ADD_LISTENER_METHOD, // add method of a listener interface + REMOVE_LISTENER_METHOD, // remove method of a listener interface + INVALID_METHOD // method whose class is not considered, e.g. XPropertySet + }; + std::unique_ptr<MethodType[]> pMethodTypes( new MethodType[ nSourceMethodCount ] ); + std::unique_ptr<sal_Int32[]> pLocalMethodConcepts( new sal_Int32[ nSourceMethodCount ] ); + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + pMethodTypes[ i ] = STANDARD_METHOD; + pLocalMethodConcepts[ i ] = 0; + } + + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_i = pSourceMethods[i]; + sal_Int32& rMethodConcept_i = pLocalMethodConcepts[ i ]; + + // Fetch name + OUString aMethName = rxMethod_i->getName(); + + // Catalogue methods + // Filter all (?) methods of XInterface so e.g. acquire and release + // can not be called from scripting + OUString className( + rxMethod_i->getDeclaringClass()->getName()); + if (className == "com.sun.star.uno.XInterface") { + bFoundXInterface = true; + + if( bXInterfaceIsInvalid ) + { + pMethodTypes[ i ] = INVALID_METHOD; + continue; + } + else + { + if( aMethName != "queryInterface" ) + { + rMethodConcept_i |= MethodConcept::DANGEROUS; + continue; + } + } + } else if (className == "com.sun.star.uno.XAggregation") + { + if( aMethName == "setDelegator" ) + { + rMethodConcept_i |= MethodConcept::DANGEROUS; + continue; + } + } else if (className + == "com.sun.star.container.XElementAccess") + { + rMethodConcept_i |= ( NAMECONTAINER | + INDEXCONTAINER | + ENUMERATION ); + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XNameContainer") + { + rMethodConcept_i |= NAMECONTAINER; + pAccess->mbNameContainer = true; + pAccess->mbNameReplace = true; + pAccess->mbNameAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XNameReplace") + { + rMethodConcept_i |= NAMECONTAINER; + pAccess->mbNameReplace = true; + pAccess->mbNameAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XNameAccess") + { + rMethodConcept_i |= NAMECONTAINER; + pAccess->mbNameAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XIndexContainer") + { + rMethodConcept_i |= INDEXCONTAINER; + pAccess->mbIndexContainer = true; + pAccess->mbIndexReplace = true; + pAccess->mbIndexAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XIndexReplace") + { + rMethodConcept_i |= INDEXCONTAINER; + pAccess->mbIndexReplace = true; + pAccess->mbIndexAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XIndexAccess") + { + rMethodConcept_i |= INDEXCONTAINER; + pAccess->mbIndexAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XEnumerationAccess") + { + rMethodConcept_i |= ENUMERATION; + pAccess->mbEnumerationAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.reflection.XIdlArray") + { + pAccess->mbIdlArray = true; + } else if (className + == "com.sun.star.lang.XUnoTunnel") + { + pAccess->mbUnoTunnel = true; + } + + // If the name is too short, it isn't anything + if( aMethName.getLength() <= 3 ) + continue; + + // Is it a get method? + OUString aPropName; + if( aMethName.startsWith("get", &aPropName) ) + { + // Get methods must not have any parameters + Sequence< Reference<XIdlClass> > getParams = rxMethod_i->getParameterTypes(); + if( getParams.hasElements() ) + { + continue; + } + + // Do we have the name already? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if (aIt != rPropNameMap.end()) + { + /* TODO + SAL_INFO("stoc",( + String( "Introspection: Property \"" ) + + OOUStringToString( aPropName, CHARSET_SYSTEM ) + + String( "\" found more than once" ) ); + */ + continue; + } + + // It is already at least a read-only property + rMethodConcept_i |= PROPERTY; + + pMethodTypes[i] = GETSET_METHOD; + Reference<XIdlClass> xGetRetType = rxMethod_i->getReturnType(); + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Write it in its property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = aPropName; + rProp.Handle = rPropCount; + rProp.Type = Type( xGetRetType->getTypeClass(), xGetRetType->getName() ); + rProp.Attributes = READONLY; + + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember get method + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq1, + rInterfaces1, rPropCount ); + rInterfaces1[ rPropCount ] = rxMethod_i; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_GETSET; + rPropertyConceptArray[ rPropCount ] = METHODS; + pAccess->mnMethodPropCount++; + + // Search for matching set method + sal_Int32 k; + for( k = 0 ; k < nSourceMethodCount ; k++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_k = pSourceMethods[k]; + + // Accept only methods that are not already assigned + if( k == i || pMethodTypes[k] != STANDARD_METHOD ) + continue; + + // Get name and evaluate + OUString aMethName2 = rxMethod_k->getName(); + OUString aPropName2; + if (!(aMethName2.startsWith("set", &aPropName2) + && aPropName2 == aPropName)) + continue; + + // A set method must return void + Reference<XIdlClass> xSetRetType = rxMethod_k->getReturnType(); + if( xSetRetType->getTypeClass() != TypeClass_VOID ) + { + continue; + } + + // A set method may only have one parameter + Sequence< Reference<XIdlClass> > setParams = rxMethod_k->getParameterTypes(); + sal_Int32 nParamCount = setParams.getLength(); + if( nParamCount != 1 ) + { + continue; + } + + // Next, the return type must correspond to the parameter type + const Reference<XIdlClass>* pParamArray2 = setParams.getConstArray(); + Reference<XIdlClass> xParamType = pParamArray2[ 0 ]; + if( xParamType->equals( xGetRetType ) ) + { + pLocalMethodConcepts[ k ] = PROPERTY; + + pMethodTypes[k] = GETSET_METHOD; + + // Delete read-only flag again + rProp.Attributes &= ~READONLY; + + // Remember set method + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq2, + rInterfaces2, rPropCount ); + rInterfaces2[ rPropCount ] = rxMethod_k; + } + } + + // Adjust count + rPropCount++; + } + + // Is it an add listener method? + else if( aMethName.startsWith("add", &aPropName) ) + { + // Does it end with "Listener"? + OUString aListenerName; + if( !aPropName.endsWith("Listener", &aListenerName) ) + continue; + + // TODO: More accurate tests could still be carried out here + // - Return type + // - Number and type of parameters + + + // Search for matching remove method, otherwise not applicable + sal_Int32 k; + for( k = 0 ; k < nSourceMethodCount ; k++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_k = pSourceMethods[k]; + + // Accept only methods that are not already assigned + if( k == i || pMethodTypes[k] != STANDARD_METHOD ) + continue; + + // Get name and evaluate + OUString aMethName2 = rxMethod_k->getName(); + OUString aListenerName2; + if (!(aMethName2.startsWith( + "remove", &aPropName) + && aPropName.endsWith( + "Listener", &aListenerName2) + && aListenerName2 == aListenerName)) + continue; + + // TODO: More accurate tests could still be carried out here + // - Return type + // - Number and type of parameters + + + // Methods are recognised as a listener interface + rMethodConcept_i |= LISTENER; + pLocalMethodConcepts[ k ] |= LISTENER; + + pMethodTypes[i] = ADD_LISTENER_METHOD; + pMethodTypes[k] = REMOVE_LISTENER_METHOD; + } + } + } + + + // A set method could still exist without a corresponding get method, + // this must be a write-only property + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_i = pSourceMethods[i]; + + // Accept only methods that are not already assigned + if( pMethodTypes[i] != STANDARD_METHOD ) + continue; + + // Get name + OUString aMethName = rxMethod_i->getName(); + + // If the name is too short, it isn't anything + if( aMethName.getLength() <= 3 ) + continue; + + // Is it a set method without associated get method? + OUString aPropName; + if( aMethName.startsWith("set", &aPropName) ) + { + // A set method must return void + Reference<XIdlClass> xSetRetType = rxMethod_i->getReturnType(); + if( xSetRetType->getTypeClass() != TypeClass_VOID ) + { + continue; + } + + // A set method may only have one parameter + Sequence< Reference<XIdlClass> > setParams = rxMethod_i->getParameterTypes(); + sal_Int32 nParamCount = setParams.getLength(); + if( nParamCount != 1 ) + { + continue; + } + + // Do we have the name already? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if (aIt != rPropNameMap.end()) + { + /* TODO: + SAL_INFO("stoc",( + String( "Introspection: Property \"" ) + + OOUStringToString( aPropName, CHARSET_SYSTEM ) + + String( "\" found more than once" ) ); + */ + continue; + } + + // Now we know it's a write only property + pLocalMethodConcepts[ i ] = PROPERTY; + + pMethodTypes[i] = GETSET_METHOD; + Reference<XIdlClass> xGetRetType = setParams.getConstArray()[0]; + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Write it in its property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = aPropName; + rProp.Handle = rPropCount; + rProp.Type = Type( xGetRetType->getTypeClass(), xGetRetType->getName() ); + rProp.Attributes = 0; // PROPERTY_WRITEONLY ??? + + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember set method + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq2, + rInterfaces2, rPropCount ); + rInterfaces2[ rPropCount ] = rxMethod_i; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_SETONLY; + rPropertyConceptArray[ rPropCount ] = METHODS; + pAccess->mnMethodPropCount++; + + // Adjust count + rPropCount++; + } + } + + + // 4. Place methods in overall sequence + + // How many methods in the method sequence + sal_Int32 nExportedMethodCount = 0; + sal_Int32 nSupportedListenerCount = 0; + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + if( pMethodTypes[ i ] != INVALID_METHOD ) + { + nExportedMethodCount++; + } + if( pMethodTypes[ i ] == ADD_LISTENER_METHOD ) + { + nSupportedListenerCount++; + } + } + + // Enlarge sequences in the access object accordingly + pAccess->maAllMethodSeq.resize( nExportedMethodCount + iAllExportedMethod ); + pAccess->maMethodConceptSeq.resize( nExportedMethodCount + iAllExportedMethod ); + pAccess->maSupportedListenerSeq.resize( nSupportedListenerCount + iAllSupportedListener ); + + // Write in methods + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + if( pMethodTypes[ i ] != INVALID_METHOD ) + { + // Address method + const Reference<XIdlMethod>& rxMethod = pSourceMethods[i]; + + // Enter name in hash table if not already known + OUString aMethName2 = rxMethod->getName(); + IntrospectionNameMap::iterator aIt = rMethodNameMap.find( aMethName2 ); + if( aIt == rMethodNameMap.end() ) + { + // Enter + rMethodNameMap[ aMethName2 ] = iAllExportedMethod; + + // Maintain table for XExactName + rLowerToExactNameMap[ aMethName2.toAsciiLowerCase() ] = aMethName2; + } + else + { + sal_Int32 iHashResult = aIt->second; + + Reference<XIdlMethod> xExistingMethod = pAccess->maAllMethodSeq[iHashResult]; + + Reference< XIdlClass > xExistingMethClass = + xExistingMethod->getDeclaringClass(); + Reference< XIdlClass > xNewMethClass = rxMethod->getDeclaringClass(); + if( xExistingMethClass->equals( xNewMethClass ) ) + continue; + } + + pAccess->maAllMethodSeq[iAllExportedMethod] = rxMethod; + + // If a concept has been set, is the method "normal"? + sal_Int32& rMethodConcept_i = pLocalMethodConcepts[ i ]; + if( !rMethodConcept_i ) + rMethodConcept_i = MethodConcept_NORMAL_IMPL; + pAccess->maMethodConceptSeq[ iAllExportedMethod ] = rMethodConcept_i; + iAllExportedMethod++; + } + if( pMethodTypes[ i ] == ADD_LISTENER_METHOD ) + { + // Determine class of listener + const Reference<XIdlMethod>& rxMethod = pSourceMethods[i]; + + // Enter void as default class + css::uno::Reference<css::reflection::XIdlClass> + xListenerClass( + reflection->forName( + cppu::UnoType<void>::get() + .getTypeName())); + // Old: Reference<XIdlClass> xListenerClass = Void_getReflection()->getIdlClass(); + + // Option 1: Search for parameters for a listener class + // Disadvantage: Superclasses should be searched recursively + const Sequence< Reference<XIdlClass> > aParams = rxMethod->getParameterTypes(); + + css::uno::Reference<css::reflection::XIdlClass> + xEventListenerClass( + reflection->forName( + cppu::UnoType< + css::lang::XEventListener>::get() + .getTypeName())); + // Old: Reference<XIdlClass> xEventListenerClass = XEventListener_getReflection()->getIdlClass(); + auto pParam = std::find_if(aParams.begin(), aParams.end(), + [&xEventListenerClass](const Reference<XIdlClass>& rxClass) { + // Are we derived from a listener? + return rxClass->equals( xEventListenerClass ) + || isDerivedFrom( rxClass, xEventListenerClass ); + }); + if (pParam != aParams.end()) + { + xListenerClass = *pParam; + } + + // Option 2: Unload the name of the method + // Disadvantage: Does not work with test listeners, where it does not exist + //aMethName = rxMethod->getName(); + //aListenerName = aMethName.Copy( 3, aMethName.Len()-8-3 ); + //Reference<XIdlClass> xListenerClass = reflection->forName( aListenerName ); + Type aListenerType( TypeClass_INTERFACE, xListenerClass->getName() ); + pAccess->maSupportedListenerSeq[ iAllSupportedListener ] = aListenerType; + iAllSupportedListener++; + } + } + + // When there were XInterface methods in this run, + // ignore them in the future + if( bFoundXInterface ) + bXInterfaceIsInvalid = true; + } + + // Do superclasses exist? Then continue here + Sequence< Reference<XIdlClass> > aSuperClassSeq = xImplClass2->getSuperclasses(); + + // Currently only one superclass is considered + if( aSuperClassSeq.getLength() >= 1 ) + { + xImplClass2 = aSuperClassSeq.getConstArray()[0]; + OSL_ENSURE( xImplClass2.is(), "super class null" ); + } + else + { + xImplClass2 = nullptr; + } + } + } + + // Apply number of exported methods and adapt Sequences + // (can be different because duplicate methods are thrown + // out only after the determination of nExportedMethodCount) + sal_Int32& rMethCount = pAccess->mnMethCount; + rMethCount = iAllExportedMethod; + pAccess->maAllMethodSeq.resize( rMethCount ); + pAccess->maMethodConceptSeq.resize( rMethCount ); + + // Resize the property sequences + pAccess->maAllPropertySeq.resize( rPropCount ); + pAccess->maPropertyConceptSeq.resize( rPropCount ); + pAccess->maMapTypeSeq.resize( rPropCount ); + } + // Register struct fields as properties + else //if( eType == TypeClass_STRUCT ) + { + // Is it an interface or a struct? + //Reference<XIdlClass> xClassRef = aToInspectObj.getReflection()->getIdlClass(); + css::uno::Reference<css::reflection::XIdlClass> xClassRef( + reflection->forName(aToInspectObj.getValueTypeName())); + if( !xClassRef.is() ) + { + SAL_WARN( "stoc", "Can't get XIdlClass from Reflection" ); + return new ImplIntrospectionAccess(aToInspectObj, pAccess); + } + + // Get fields + const Sequence< Reference<XIdlField> > fields = xClassRef->getFields(); + + for( const Reference<XIdlField>& xField : fields ) + { + Reference<XIdlClass> xPropType = xField->getType(); + OUString aPropName = xField->getName(); + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Write it in its property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = aPropName; + rProp.Handle = rPropCount; + rProp.Type = Type( xPropType->getTypeClass(), xPropType->getName() ); + FieldAccessMode eAccessMode = xField->getAccessMode(); + rProp.Attributes = (eAccessMode == FieldAccessMode_READONLY || + eAccessMode == FieldAccessMode_CONST) + ? READONLY : 0; + + //FieldAccessMode eAccessMode = xField->getAccessMode(); + //rProp.Attributes = (eAccessMode == FieldAccessMode::READONLY || eAccessMode == CONST) + //? PropertyAttribute::READONLY : 0; + + // Write name in hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember field + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq1, + rInterfaces1, rPropCount ); + rInterfaces1[ rPropCount ] = xField; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_FIELD; + rPropertyConceptArray[ rPropCount ] = ATTRIBUTES; + pAccess->mnAttributePropCount++; + + // Adjust count + rPropCount++; + } + } + + // Set property sequence to the correct length + pAccess->maAllPropertySeq.resize( pAccess->mnPropCount ); + + return new ImplIntrospectionAccess(aToInspectObj, pAccess); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_Introspection_get_implementation( + css::uno::XComponentContext * context, + css::uno::Sequence<css::uno::Any> const & arguments) +{ + SAL_WARN_IF( + arguments.hasElements(), "stoc", "unexpected singleton arguments"); + return cppu::acquire(new Implementation(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/invocation/invocation.component b/stoc/source/invocation/invocation.component new file mode 100644 index 0000000000..b5027a4d6d --- /dev/null +++ b/stoc/source/invocation/invocation.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.Invocation" + constructor="stoc_InvocationService_get_implementation"> + <service name="com.sun.star.script.Invocation"/> + </implementation> +</component> diff --git a/stoc/source/invocation/invocation.cxx b/stoc/source/invocation/invocation.cxx new file mode 100644 index 0000000000..7da24b3ae0 --- /dev/null +++ b/stoc/source/invocation/invocation.cxx @@ -0,0 +1,1091 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <comphelper/sequence.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/script/XInvocation2.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> + +#include <memory> +#include <vector> + +using namespace css::uno; +using namespace css::lang; +using namespace css::script; +using namespace css::reflection; +using namespace css::beans; +using namespace css::container; +using namespace cppu; +using namespace osl; + +namespace stoc_inv +{ + + +// TODO: Implement centrally +static Reference<XIdlClass> TypeToIdlClass( const Type& rType, const Reference< XIdlReflection > & xRefl ) +{ + return xRefl->forName( rType.getTypeName() ); +} + +namespace { + +class Invocation_Impl + : public OWeakObject + , public XInvocation2 + , public XNameContainer + , public XIndexContainer + , public XEnumerationAccess + , public XExactName + , public XMaterialHolder + , public XTypeProvider +{ +public: + Invocation_Impl( const Any & rAdapted, const Reference<XTypeConverter> &, + const Reference<XIntrospection> &, + const Reference<XIdlReflection> &, + bool bFromOLE ); + + // XInterface + virtual Any SAL_CALL queryInterface( const Type & aType) override; + virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override { OWeakObject::release(); } + + + // XTypeProvider + virtual Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XMaterialHolder + virtual Any SAL_CALL getMaterial() override; + + // XInvocation + virtual Reference<XIntrospectionAccess> SAL_CALL getIntrospection() override; + virtual Any SAL_CALL invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& OutParamIndex, Sequence< Any >& OutParam) override; + virtual void SAL_CALL setValue(const OUString& PropertyName, const Any& Value) override; + virtual Any SAL_CALL getValue(const OUString& PropertyName) override; + virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override; + virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override; + + // XInvocation2 + virtual Sequence< OUString > SAL_CALL getMemberNames( ) override; + virtual Sequence< InvocationInfo > SAL_CALL getInfo( ) override; + virtual InvocationInfo SAL_CALL getInfoForName( const OUString& aName, sal_Bool bExact ) override; + + // All Access and Container methods are not thread safe + // XElementAccess + virtual Type SAL_CALL getElementType() override + { return _xElementAccess->getElementType(); } + + virtual sal_Bool SAL_CALL hasElements() override + { return _xElementAccess->hasElements(); } + + // XNameContainer + virtual void SAL_CALL insertByName( const OUString& Name, const Any& Element ) override + { _xNameContainer->insertByName( Name, Element ); } + + virtual void SAL_CALL removeByName( const OUString& Name ) override + { _xNameContainer->removeByName( Name ); } + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& Name, const Any& Element ) override + { _xNameReplace->replaceByName( Name, Element ); } + + // XNameAccess + virtual Any SAL_CALL getByName( const OUString& Name ) override + { return _xNameAccess->getByName( Name ); } + + virtual Sequence<OUString> SAL_CALL getElementNames() override + { return _xNameAccess->getElementNames(); } + + virtual sal_Bool SAL_CALL hasByName( const OUString& Name ) override + { return _xNameAccess->hasByName( Name ); } + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const Any& Element ) override + { _xIndexContainer->insertByIndex( Index, Element ); } + + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override + { _xIndexContainer->removeByIndex( Index ); } + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const Any& Element ) override + { _xIndexReplace->replaceByIndex( Index, Element ); } + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override + { return _xIndexAccess->getCount(); } + + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override + { return _xIndexAccess->getByIndex( Index ); } + + // XEnumerationAccess + virtual Reference<XEnumeration> SAL_CALL createEnumeration() override + { return _xEnumerationAccess->createEnumeration(); } + + // XExactName + virtual OUString SAL_CALL getExactName( const OUString& rApproximateName ) override; + + +private: + void setMaterial( const Any& rMaterial ); + + void getInfoSequenceImpl( Sequence< OUString >* pStringSeq, Sequence< InvocationInfo >* pInfoSeq ); + void fillInfoForNameAccess( InvocationInfo& rInfo, const OUString& aName ); + static void fillInfoForProperty( InvocationInfo& rInfo, const Property& rProp ); + static void fillInfoForMethod( InvocationInfo& rInfo, const Reference< XIdlMethod >& xMethod ); + + Reference<XTypeConverter> xTypeConverter; + Reference<XIntrospection> xIntrospection; + Reference<XIdlReflection> xCoreReflection; + + Any _aMaterial; + // _xDirect and (_xIntrospectionAccess, xPropertySet) are exclusive + Reference<XInvocation> _xDirect; + Reference<XInvocation2> _xDirect2; + Reference<XPropertySet> _xPropertySet; + Reference<XIntrospectionAccess> _xIntrospectionAccess; + + // supplied Interfaces + Reference<XNameContainer> _xNameContainer; + Reference<XNameReplace> _xNameReplace; + Reference<XNameAccess> _xNameAccess; + Reference<XIndexContainer> _xIndexContainer; + Reference<XIndexReplace> _xIndexReplace; + Reference<XIndexAccess> _xIndexAccess; + Reference<XEnumerationAccess> _xEnumerationAccess; + Reference<XElementAccess> _xElementAccess; + + + Reference<XExactName> _xENDirect, _xENIntrospection; + + bool mbFromOLE; +}; + +} + +Invocation_Impl::Invocation_Impl +( + const Any & rAdapted, + const Reference<XTypeConverter> & rTC, + const Reference<XIntrospection> & rI, + const Reference<XIdlReflection> & rCR, + bool bFromOLE +) + : xTypeConverter( rTC ) + , xIntrospection( rI ) + , xCoreReflection( rCR ) + , mbFromOLE( bFromOLE ) +{ + setMaterial( rAdapted ); +} + +//### INTERFACE IMPLEMENTATIONS #################################################################### + + +Any SAL_CALL Invocation_Impl::queryInterface( const Type & aType ) +{ + // PropertySet implementation + Any a = ::cppu::queryInterface( aType, + static_cast< XInvocation* >(this), + static_cast< XMaterialHolder* >(this), + static_cast< XTypeProvider * >(this) ); + if( a.hasValue() ) + { + return a; + } + + if( aType == cppu::UnoType<XExactName>::get()) + { + // Invocation does not support XExactName, if direct object supports + // XInvocation, but not XExactName. Except when called from OLE Automation. + if (mbFromOLE || + (_xDirect.is() && _xENDirect.is()) || + (!_xDirect.is() && _xENIntrospection.is())) + { + return Any( Reference< XExactName >( static_cast< XExactName* >(this) ) ); + } + } + else if ( aType == cppu::UnoType<XNameContainer>::get()) + { + if( _xNameContainer.is() ) + return Any( Reference< XNameContainer >( static_cast< XNameContainer* >(this) ) ); + } + else if ( aType == cppu::UnoType<XNameReplace>::get()) + { + if( _xNameReplace.is() ) + return Any( Reference< XNameReplace >( static_cast< XNameReplace* >(this) ) ); + } + else if ( aType == cppu::UnoType<XNameAccess>::get()) + { + if( _xNameAccess.is() ) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >(this) ) ); + } + else if ( aType == cppu::UnoType<XIndexContainer>::get()) + { + if (_xIndexContainer.is()) + return Any( Reference< XIndexContainer >( static_cast< XIndexContainer* >(this) ) ); + } + else if ( aType == cppu::UnoType<XIndexReplace>::get()) + { + if (_xIndexReplace.is()) + return Any( Reference< XIndexReplace >( static_cast< XIndexReplace* >(this) ) ); + } + else if ( aType == cppu::UnoType<XIndexAccess>::get()) + { + if (_xIndexAccess.is()) + return Any( Reference< XIndexAccess >( static_cast< XIndexAccess* >(this) ) ); + } + else if ( aType == cppu::UnoType<XEnumerationAccess>::get()) + { + if (_xEnumerationAccess.is()) + return Any( Reference< XEnumerationAccess >( static_cast< XEnumerationAccess* >(this) ) ); + } + else if ( aType == cppu::UnoType<XElementAccess>::get()) + { + if (_xElementAccess.is()) + { + return Any( Reference< XElementAccess >( + static_cast< XElementAccess* >(static_cast< XNameContainer* >(this)) ) ); + } + } + else if ( aType == cppu::UnoType<XInvocation2>::get()) + { + // Invocation does not support XInvocation2, if direct object supports + // XInvocation, but not XInvocation2. + if ( mbFromOLE || + ( _xDirect.is() && _xDirect2.is()) || + (!_xDirect.is() && _xIntrospectionAccess.is() ) ) + { + return Any( Reference< XInvocation2 >( static_cast< XInvocation2* >(this) ) ); + } + } + + return OWeakObject::queryInterface( aType ); +} + + +Any Invocation_Impl::getMaterial() +{ + // AB, 12.2.1999 Make sure that the material is taken when possible + // from the direct Invocation of the Introspection, otherwise structs + // are not handled correctly + Reference<XMaterialHolder> xMaterialHolder; + if( _xDirect.is() ) + { + xMaterialHolder.set( _xDirect, UNO_QUERY ); + //_xDirect->queryInterface( XMaterialHolder::getSmartUik(), xMaterialHolder ); + } + else if( _xIntrospectionAccess.is() ) + { + xMaterialHolder.set( _xIntrospectionAccess, UNO_QUERY ); + //_xIntrospectionAccess->queryInterface( XMaterialHolder::getSmartUik(), xMaterialHolder ); + } + if( xMaterialHolder.is() ) + { + return xMaterialHolder->getMaterial(); + } + return _aMaterial; +} + + +void Invocation_Impl::setMaterial( const Any& rMaterial ) +{ + // set the material first and only once + _aMaterial = rMaterial; + + // First do this outside the guard + _xDirect.set( rMaterial, UNO_QUERY ); + + if( !mbFromOLE && _xDirect.is() ) + { + // Consult object directly + _xElementAccess.set( _xDirect, UNO_QUERY ); + _xEnumerationAccess.set( _xDirect, UNO_QUERY ); + _xIndexAccess.set( _xDirect, UNO_QUERY ); + _xIndexReplace.set( _xDirect, UNO_QUERY ); + _xIndexContainer.set( _xDirect, UNO_QUERY ); + _xNameAccess.set( _xDirect, UNO_QUERY ); + _xNameReplace.set( _xDirect, UNO_QUERY ); + _xNameContainer.set( _xDirect, UNO_QUERY ); + _xENDirect.set( _xDirect, UNO_QUERY ); + _xDirect2.set( _xDirect, UNO_QUERY ); + } + else + { + // Make Invocation on the Introspection + if (xIntrospection.is()) + { + _xIntrospectionAccess = xIntrospection->inspect( _aMaterial ); + if( _xIntrospectionAccess.is() ) + { + _xElementAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XElementAccess>::get()), UNO_QUERY ); + + if( _xElementAccess.is() ) + { + _xEnumerationAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XEnumerationAccess>::get()), UNO_QUERY ); + + _xIndexAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XIndexAccess>::get()), UNO_QUERY ); + + if( _xIndexAccess.is() ) + { + _xIndexReplace.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XIndexReplace>::get()), UNO_QUERY ); + + _xIndexContainer.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XIndexContainer>::get()), UNO_QUERY ); + } + + _xNameAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XNameAccess>::get()), UNO_QUERY ); + + if( _xNameAccess.is() ) + { + _xNameReplace.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XNameReplace>::get()), UNO_QUERY ); + + _xNameContainer.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XNameContainer>::get()), UNO_QUERY ); + } + } + + _xPropertySet.set( _xIntrospectionAccess->queryAdapter( cppu::UnoType<XPropertySet>::get()), + UNO_QUERY ); + + _xENIntrospection.set( _xIntrospectionAccess, UNO_QUERY ); + } + } + } +} + + +OUString Invocation_Impl::getExactName( const OUString& rApproximateName ) +{ + if (_xENDirect.is()) + return _xENDirect->getExactName( rApproximateName ); + + OUString aRet; + if (_xENIntrospection.is()) + aRet = _xENIntrospection->getExactName( rApproximateName ); + return aRet; +} + + +Reference<XIntrospectionAccess> Invocation_Impl::getIntrospection() +{ + if( _xDirect.is() ) + return _xDirect->getIntrospection(); + else + return _xIntrospectionAccess; +} + + +sal_Bool Invocation_Impl::hasMethod( const OUString& Name ) +{ + if (!mbFromOLE && _xDirect.is()) + return _xDirect->hasMethod( Name ); + if( _xIntrospectionAccess.is() ) + return _xIntrospectionAccess->hasMethod( Name, MethodConcept::ALL ^ MethodConcept::DANGEROUS ); + return false; +} + + +sal_Bool Invocation_Impl::hasProperty( const OUString& Name ) +{ + if (_xDirect.is()) + { + bool bRet = _xDirect->hasProperty( Name ); + if (bRet || !mbFromOLE) + return bRet; + } + // PropertySet + if( _xIntrospectionAccess.is() + && _xIntrospectionAccess->hasProperty( Name, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + return true; + // NameAccess + if( _xNameAccess.is() ) + return _xNameAccess->hasByName( Name ); + return false; +} + + +Any Invocation_Impl::getValue( const OUString& PropertyName ) +{ + try + { + if (_xDirect.is()) + return _xDirect->getValue( PropertyName ); + } + catch (Exception &) + { + if (!mbFromOLE) + throw; + } + try + { + // PropertySet + if( _xIntrospectionAccess.is() && _xPropertySet.is() + && _xIntrospectionAccess->hasProperty + ( PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + { + return _xPropertySet->getPropertyValue( PropertyName ); + } + // NameAccess + if( _xNameAccess.is() && _xNameAccess->hasByName( PropertyName ) ) + return _xNameAccess->getByName( PropertyName ); + } + catch (UnknownPropertyException &) + { + throw; + } + catch (RuntimeException &) + { + throw; + } + catch (Exception &) + { + } + + throw UnknownPropertyException( "cannot get value " + PropertyName ); +} + + +void Invocation_Impl::setValue( const OUString& PropertyName, const Any& Value ) +{ + try + { + if (_xDirect.is()) + { + _xDirect->setValue( PropertyName, Value ); + return; + } + } + catch (Exception &) + { + if (!mbFromOLE) + throw; + } + try + { + // Properties + if( _xIntrospectionAccess.is() && _xPropertySet.is() + && _xIntrospectionAccess->hasProperty( + PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + { + Property aProp = _xIntrospectionAccess->getProperty( + PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ); + Reference < XIdlClass > r = TypeToIdlClass( aProp.Type, xCoreReflection ); + if( r->isAssignableFrom( TypeToIdlClass( Value.getValueType(), xCoreReflection ) ) ) + _xPropertySet->setPropertyValue( PropertyName, Value ); + else if( xTypeConverter.is() ) + _xPropertySet->setPropertyValue( + PropertyName, xTypeConverter->convertTo( Value, aProp.Type ) ); + else + throw RuntimeException( "no type converter service!" ); + } + // NameContainer + else if( _xNameContainer.is() ) + { + // Note: This misfeature deliberately not adapted to apply to objects which + // have XNameReplace but not XNameContainer + Any aConv; + Reference < XIdlClass > r = + TypeToIdlClass( _xNameContainer->getElementType(), xCoreReflection ); + if( r->isAssignableFrom(TypeToIdlClass( Value.getValueType(), xCoreReflection ) ) ) + aConv = Value; + else if( xTypeConverter.is() ) + aConv = xTypeConverter->convertTo( Value, _xNameContainer->getElementType() ); + else + throw RuntimeException( "no type converter service!" ); + + // Replace if present, otherwise insert + if (_xNameContainer->hasByName( PropertyName )) + _xNameContainer->replaceByName( PropertyName, aConv ); + else + _xNameContainer->insertByName( PropertyName, aConv ); + } + else + throw UnknownPropertyException( "no introspection nor name container!" ); + } + catch (UnknownPropertyException &) + { + throw; + } + catch (CannotConvertException &) + { + throw; + } + catch (InvocationTargetException &) + { + throw; + } + catch (RuntimeException &) + { + throw; + } + catch (const Exception & exc) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw InvocationTargetException( + "exception occurred in setValue(): " + exc.Message, + Reference< XInterface >(), anyEx ); + } +} + + +Any Invocation_Impl::invoke( const OUString& FunctionName, const Sequence<Any>& InParams, + Sequence<sal_Int16>& OutIndices, Sequence<Any>& OutParams ) +{ + if (!mbFromOLE && _xDirect.is()) + return _xDirect->invoke( FunctionName, InParams, OutIndices, OutParams ); + + if (_xIntrospectionAccess.is()) + { + // throw NoSuchMethodException if not exist + Reference<XIdlMethod> xMethod = _xIntrospectionAccess->getMethod( + FunctionName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ); + + // ParameterInfos + Sequence<ParamInfo> aFParams = xMethod->getParameterInfos(); + const ParamInfo* pFParams = aFParams.getConstArray(); + sal_Int32 nFParamsLen = aFParams.getLength(); + if (nFParamsLen != InParams.getLength()) + { + throw IllegalArgumentException( + "incorrect number of parameters passed invoking function " + FunctionName + + ": expected " + OUString::number(nFParamsLen) + ", got " + OUString::number(InParams.getLength()), + getXWeak(), sal_Int16(1) ); + } + + // IN Parameter + const Any* pInParams = InParams.getConstArray(); + + // Introspection Invoke Parameter + Sequence<Any> aInvokeParams( nFParamsLen ); + Any* pInvokeParams = aInvokeParams.getArray(); + + // OUT Indices + OutIndices.realloc( nFParamsLen ); + sal_Int16* pOutIndices = OutIndices.getArray(); + sal_uInt32 nOutIndex = 0; + + for ( sal_Int32 nPos = 0; nPos < nFParamsLen; ++nPos ) + { + try + { + const ParamInfo& rFParam = pFParams[nPos]; + const Reference<XIdlClass>& rDestType = rFParam.aType; + + // is IN/INOUT parameter? + if (rFParam.aMode != ParamMode_OUT) + { + if (rDestType->isAssignableFrom( TypeToIdlClass( pInParams[nPos].getValueType(), xCoreReflection ) )) + { + pInvokeParams[nPos] = pInParams[nPos]; + } + else if (xTypeConverter.is()) + { + Type aDestType( rDestType->getTypeClass(), rDestType->getName() ); + pInvokeParams[nPos] = xTypeConverter->convertTo( pInParams[nPos], aDestType ); + } + else + { + throw CannotConvertException("invocation type mismatch!", *this, {}, 0, 0); + } + } + + // is OUT/INOUT parameter? + if (rFParam.aMode != ParamMode_IN) + { + pOutIndices[nOutIndex] = static_cast<sal_Int16>(nPos); + if (rFParam.aMode == ParamMode_OUT) + rDestType->createObject( pInvokeParams[nPos] ); // default init + ++nOutIndex; + } + } + catch( CannotConvertException& rExc ) + { + rExc.ArgumentIndex = nPos; // Add optional parameter index + throw; + } + } + + // execute Method + Any aRet = xMethod->invoke( _aMaterial, aInvokeParams ); + + // OUT Params + OutIndices.realloc( nOutIndex ); + OutParams.realloc( nOutIndex ); + + std::transform(std::cbegin(OutIndices), std::cend(OutIndices), OutParams.getArray(), + [&pInvokeParams](const sal_Int16 nIndex) -> Any { return pInvokeParams[nIndex]; }); + + return aRet; + } + + throw RuntimeException("invocation lacking of introspection access!", *this); +} + +namespace { + +// Struct to optimize sorting +struct MemberItem +{ + OUString aName; + + // Defines where the member comes from + enum class Mode { NameAccess, PropertySet, Method }; + Mode eMode; + + // Index to respective sequence + // (Index to NameAccess sequence for eMode==Mode::NameAccess etc.) + sal_Int32 nIndex; +}; + +} + +// Implementation of getting name or info +// String sequence will be filled when pStringSeq != NULL +// Info sequence will be filled when pInfoSeq != NULL +void Invocation_Impl::getInfoSequenceImpl +( + Sequence< OUString >* pStringSeq, + Sequence< InvocationInfo >* pInfoSeq +) +{ + //Sequence< OUString > aStrSeq; + //if( !pStringSeq ) + //pStringSeq = &aStrSeq; + + + // Get all needed sequences + Sequence<OUString> aNameAccessNames; + Sequence<Property> aPropertySeq; + Sequence< Reference< XIdlMethod > > aMethodSeq; + + if( _xNameAccess.is() ) + { + aNameAccessNames = _xNameAccess->getElementNames(); + } + + if( _xIntrospectionAccess.is() ) + { + aPropertySeq = _xIntrospectionAccess->getProperties + ( PropertyConcept::ALL - PropertyConcept::DANGEROUS ); + + aMethodSeq = _xIntrospectionAccess->getMethods + ( MethodConcept::ALL - MethodConcept::DANGEROUS ); + } + + sal_Int32 nNameAccessCount = aNameAccessNames.getLength(); + sal_Int32 nPropertyCount = aPropertySeq.getLength(); + sal_Int32 nMethodCount = aMethodSeq.getLength(); + sal_Int32 nTotalCount = nNameAccessCount + nPropertyCount + nMethodCount; + + // Create and fill array of MemberItems + std::unique_ptr< MemberItem []> pItems( new MemberItem[ nTotalCount ] ); + const OUString* pStrings = aNameAccessNames.getConstArray(); + const Property* pProps = aPropertySeq.getConstArray(); + const Reference< XIdlMethod >* pMethods = aMethodSeq.getConstArray(); + + // Fill array of MemberItems + sal_Int32 i, iTotal = 0; + + // Name Access + for( i = 0 ; i < nNameAccessCount ; i++, iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + rItem.aName = pStrings[ i ]; + rItem.eMode = MemberItem::Mode::NameAccess; + rItem.nIndex = i; + } + + // Property set + for( i = 0 ; i < nPropertyCount ; i++, iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + rItem.aName = pProps[ i ].Name; + rItem.eMode = MemberItem::Mode::PropertySet; + rItem.nIndex = i; + } + + // Methods + for( i = 0 ; i < nMethodCount ; i++, iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + Reference< XIdlMethod > xMethod = pMethods[ i ]; + rItem.aName = xMethod->getName(); + rItem.eMode = MemberItem::Mode::Method; + rItem.nIndex = i; + } + + // Setting up result sequences + OUString* pRetStrings = nullptr; + if( pStringSeq ) + { + pStringSeq->realloc( nTotalCount ); + pRetStrings = pStringSeq->getArray(); + } + + InvocationInfo* pRetInfos = nullptr; + if( pInfoSeq ) + { + pInfoSeq->realloc( nTotalCount ); + pRetInfos = pInfoSeq->getArray(); + } + + // Fill result sequences in the correct order of members + for( iTotal = 0 ; iTotal < nTotalCount ; iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + if( pRetStrings ) + { + pRetStrings[ iTotal ] = rItem.aName; + } + + if( pRetInfos ) + { + if( rItem.eMode == MemberItem::Mode::NameAccess ) + { + fillInfoForNameAccess( pRetInfos[ iTotal ], rItem.aName ); + } + else if( rItem.eMode == MemberItem::Mode::PropertySet ) + { + fillInfoForProperty( pRetInfos[ iTotal ], pProps[ rItem.nIndex ] ); + } + else if( rItem.eMode == MemberItem::Mode::Method ) + { + fillInfoForMethod( pRetInfos[ iTotal ], pMethods[ rItem.nIndex ] ); + } + } + } +} + +// XInvocation2 +Sequence< OUString > SAL_CALL Invocation_Impl::getMemberNames( ) +{ + if( _xDirect2.is() ) + { + return _xDirect2->getMemberNames(); + } + Sequence< OUString > aRetSeq; + getInfoSequenceImpl( &aRetSeq, nullptr ); + return aRetSeq; +} + +Sequence< InvocationInfo > SAL_CALL Invocation_Impl::getInfo( ) +{ + if( _xDirect2.is() ) + { + return _xDirect2->getInfo(); + } + Sequence< InvocationInfo > aRetSeq; + getInfoSequenceImpl( nullptr, &aRetSeq ); + return aRetSeq; +} + +InvocationInfo SAL_CALL Invocation_Impl::getInfoForName( const OUString& aName, sal_Bool bExact ) +{ + if( _xDirect2.is() ) + { + return _xDirect2->getInfoForName( aName, bExact ); + } + + bool bFound = false; + OUString aExactName = aName; + InvocationInfo aRetInfo; + + if( bExact ) + aExactName = getExactName( aName ); + if( !aExactName.isEmpty() ) + { + if( _xIntrospectionAccess->hasMethod( aExactName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ) ) + { + Reference<XIdlMethod> xMethod = _xIntrospectionAccess->getMethod + ( aExactName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ); + fillInfoForMethod( aRetInfo, xMethod ); + bFound = true; + } + else + { + if( _xIntrospectionAccess.is() && _xIntrospectionAccess->hasProperty + ( aExactName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + { + Property aProp = _xIntrospectionAccess->getProperty + ( aExactName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ); + fillInfoForProperty( aRetInfo, aProp ); + bFound = true; + } + // NameAccess + else if( _xNameAccess.is() && _xNameAccess->hasByName( aExactName ) ) + { + fillInfoForNameAccess( aRetInfo, aExactName ); + bFound = true; + } + } + } + if( !bFound ) + { + throw IllegalArgumentException( + "getExactName(), Unknown name " + aName, + getXWeak(), 0 ); + } + return aRetInfo; +} + +// Helper functions to fill InvocationInfo for XNameAccess +void Invocation_Impl::fillInfoForNameAccess +( + InvocationInfo& rInfo, + const OUString& aName +) +{ + rInfo.aName = aName; + rInfo.eMemberType = MemberType_PROPERTY; + rInfo.PropertyAttribute = 0; + if( !_xNameContainer.is() ) + { + rInfo.PropertyAttribute = PropertyAttribute::READONLY; + } + rInfo.aType = _xNameAccess->getElementType(); +} + +void Invocation_Impl::fillInfoForProperty +( + InvocationInfo& rInfo, + const Property& rProp +) +{ + rInfo.aName = rProp.Name; + rInfo.eMemberType = MemberType_PROPERTY; + rInfo.PropertyAttribute = rProp.Attributes; + rInfo.aType = rProp.Type; +} + +void Invocation_Impl::fillInfoForMethod +( + InvocationInfo& rInfo, + const Reference< XIdlMethod >& xMethod +) +{ + rInfo.aName = xMethod->getName(); + rInfo.eMemberType = MemberType_METHOD; + Reference< XIdlClass > xReturnClass = xMethod->getReturnType(); + Type aReturnType( xReturnClass->getTypeClass(), xReturnClass->getName() ); + rInfo.aType = aReturnType; + Sequence<ParamInfo> aParamInfos = xMethod->getParameterInfos(); + sal_Int32 nParamCount = aParamInfos.getLength(); + if( nParamCount <= 0 ) + return; + + const ParamInfo* pInfo = aParamInfos.getConstArray(); + + rInfo.aParamTypes.realloc( nParamCount ); + Type* pParamTypes = rInfo.aParamTypes.getArray(); + rInfo.aParamModes.realloc( nParamCount ); + ParamMode* pParamModes = rInfo.aParamModes.getArray(); + + for( sal_Int32 i = 0 ; i < nParamCount ; i++ ) + { + Reference< XIdlClass > xParamClass = pInfo[i].aType; + Type aParamType( xParamClass->getTypeClass(), xParamClass->getName() ); + pParamTypes[ i ] = aParamType; + pParamModes[ i ] = pInfo[i].aMode; + } +} + + +// XTypeProvider +Sequence< Type > SAL_CALL Invocation_Impl::getTypes() +{ + static Sequence<Type> s_types = [this]() { + std::vector<Type> tmp { + cppu::UnoType<XTypeProvider>::get(), + cppu::UnoType<XWeak>::get(), + cppu::UnoType<XInvocation>::get(), + cppu::UnoType<XMaterialHolder>::get() }; + + // Invocation does not support XExactName if direct object supports + // XInvocation, but not XExactName. + if ((_xDirect.is() && _xENDirect.is()) || (!_xDirect.is() && _xENIntrospection.is())) + tmp.push_back(cppu::UnoType<XExactName>::get()); + if (_xNameContainer.is()) + tmp.push_back(cppu::UnoType<XNameContainer>::get()); + if (_xNameReplace.is()) + tmp.push_back(cppu::UnoType<XNameReplace>::get()); + if (_xNameAccess.is()) + tmp.push_back(cppu::UnoType<XNameAccess>::get()); + if (_xIndexContainer.is()) + tmp.push_back(cppu::UnoType<XIndexContainer>::get()); + if (_xIndexReplace.is()) + tmp.push_back(cppu::UnoType<XIndexReplace>::get()); + if (_xIndexAccess.is()) + tmp.push_back(cppu::UnoType<XIndexAccess>::get()); + if (_xEnumerationAccess.is()) + tmp.push_back(cppu::UnoType<XEnumerationAccess>::get()); + if (_xElementAccess.is()) + tmp.push_back(cppu::UnoType<XElementAccess>::get()); + // Invocation does not support XInvocation2, if direct object supports + // XInvocation, but not XInvocation2. + if ((_xDirect.is() && _xDirect2.is()) || (!_xDirect.is() && _xIntrospectionAccess.is())) + tmp.push_back(cppu::UnoType<XInvocation2>::get()); + + return comphelper::containerToSequence(tmp); + }(); + return s_types; +} + +Sequence< sal_Int8 > SAL_CALL Invocation_Impl::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +namespace { + +class InvocationService + : public WeakImplHelper< XSingleServiceFactory, XServiceInfo > +{ +public: + explicit InvocationService( const Reference<XComponentContext> & xCtx ); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XSingleServiceFactory + Reference<XInterface> SAL_CALL createInstance() override; + Reference<XInterface> SAL_CALL createInstanceWithArguments( + const Sequence<Any>& rArguments ) override; +private: + Reference<XComponentContext> mxCtx; + Reference<XMultiComponentFactory> mxSMgr; + Reference<XTypeConverter> xTypeConverter; + Reference<XIntrospection> xIntrospection; + Reference<XIdlReflection> xCoreReflection; +}; + +} + +InvocationService::InvocationService( const Reference<XComponentContext> & xCtx ) + : mxCtx( xCtx ) + , mxSMgr( xCtx->getServiceManager() ) + , xCoreReflection( css::reflection::theCoreReflection::get(mxCtx) ) +{ + xTypeConverter.set( + mxSMgr->createInstanceWithContext( "com.sun.star.script.Converter", xCtx ), + UNO_QUERY ); + xIntrospection = theIntrospection::get(xCtx); +} + +// XServiceInfo +OUString InvocationService::getImplementationName() +{ + return "com.sun.star.comp.stoc.Invocation"; +} + +// XServiceInfo +sal_Bool InvocationService::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > InvocationService::getSupportedServiceNames() +{ + return { "com.sun.star.script.Invocation" }; +} + + +Reference<XInterface> InvocationService::createInstance() +{ + //TODO:throw( Exception("no default construction of invocation adapter possible!", *this) ); + return Reference<XInterface>(); // dummy +} + + +Reference<XInterface> InvocationService::createInstanceWithArguments( + const Sequence<Any>& rArguments ) +{ + if (rArguments.getLength() == 2) + { + OUString aArg1; + if ((rArguments[1] >>= aArg1) && + aArg1 == "FromOLE") + { + return Reference< XInterface > + ( *new Invocation_Impl( *rArguments.getConstArray(), + xTypeConverter, xIntrospection, xCoreReflection, true ) ); + } + } + if (rArguments.getLength() == 1) + { + return Reference< XInterface > + ( *new Invocation_Impl( *rArguments.getConstArray(), + xTypeConverter, xIntrospection, xCoreReflection, false ) ); + } + + //TODO:throw( Exception("no default construction of invocation adapter possible!", *this) ); + return Reference<XInterface>(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_InvocationService_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new InvocationService(context)); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/invocation_adapterfactory/iafactory.cxx b/stoc/source/invocation_adapterfactory/iafactory.cxx new file mode 100644 index 0000000000..b5b62db089 --- /dev/null +++ b/stoc/source/invocation_adapterfactory/iafactory.cxx @@ -0,0 +1,881 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <osl/diagnose.h> +#include <osl/interlck.h> +#include <osl/mutex.hxx> +#include <o3tl/sorted_vector.hxx> +#include <sal/log.hxx> + +#include <uno/dispatcher.h> +#include <uno/data.h> +#include <uno/any2.h> +#include <uno/lbnames.h> +#include <uno/mapping.hxx> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory2.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/reflection/InvocationTargetException.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <unordered_map> +#include <vector> + +using namespace ::osl; +using namespace ::com::sun::star; +using namespace css::uno; + +namespace stoc_invadp +{ + + +namespace { + +struct hash_ptr +{ + size_t operator() ( void * p ) const + { return reinterpret_cast<size_t>(p); } +}; + +} + +typedef o3tl::sorted_vector< void * > t_ptr_set; +typedef std::unordered_map< void *, t_ptr_set, hash_ptr > t_ptr_map; + +namespace { + +class FactoryImpl + : public ::cppu::WeakImplHelper< lang::XServiceInfo, + script::XInvocationAdapterFactory, + script::XInvocationAdapterFactory2 > +{ +public: + Mapping m_aUno2Cpp; + Mapping m_aCpp2Uno; + uno_Interface * m_pConverter; + + typelib_TypeDescription * m_pInvokMethodTD; + typelib_TypeDescription * m_pSetValueTD; + typelib_TypeDescription * m_pGetValueTD; + typelib_TypeDescription * m_pAnySeqTD; + typelib_TypeDescription * m_pShortSeqTD; + typelib_TypeDescription * m_pConvertToTD; + + Mutex m_mutex; + t_ptr_map m_receiver2adapters; + + explicit FactoryImpl( Reference< XComponentContext > const & xContext ); + virtual ~FactoryImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInvocationAdapterFactory + virtual Reference< XInterface > SAL_CALL createAdapter( + const Reference< script::XInvocation > & xReceiver, const Type & rType ) override; + // XInvocationAdapterFactory2 + virtual Reference< XInterface > SAL_CALL createAdapter( + const Reference< script::XInvocation > & xReceiver, + const Sequence< Type > & rTypes ) override; +}; +struct AdapterImpl; + +struct InterfaceAdapterImpl : public uno_Interface +{ + AdapterImpl * m_pAdapter; + typelib_InterfaceTypeDescription * m_pTypeDescr; +}; + +struct AdapterImpl +{ + oslInterlockedCount m_nRef; + FactoryImpl * m_pFactory; + void * m_key; // map key + uno_Interface * m_pReceiver; // XInvocation receiver + + std::vector<InterfaceAdapterImpl> m_vInterfaces; + + // XInvocation calls + void getValue( + const typelib_TypeDescription * pMemberType, + void * pReturn, uno_Any ** ppException ); + void setValue( + const typelib_TypeDescription * pMemberType, + void * pArgs[], uno_Any ** ppException ); + void invoke( + const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs[], uno_Any ** ppException ); + + bool coerce_assign( + void * pDest, typelib_TypeDescriptionReference * pType, + uno_Any * pSource, uno_Any * pExc ); + inline bool coerce_construct( + void * pDest, typelib_TypeDescriptionReference * pType, + uno_Any * pSource, uno_Any * pExc ); + + inline void acquire(); + inline void release(); + inline ~AdapterImpl(); + inline AdapterImpl( + void * key, Reference< script::XInvocation > const & xReceiver, + const Sequence< Type > & rTypes, + FactoryImpl * pFactory ); + + // Copy assignment is forbidden and not implemented. + AdapterImpl (const AdapterImpl &) = delete; + AdapterImpl & operator= (const AdapterImpl &) = delete; +}; + +} + +inline AdapterImpl::~AdapterImpl() +{ + for ( size_t nPos = m_vInterfaces.size(); nPos--; ) + { + ::typelib_typedescription_release( + &m_vInterfaces[ nPos ].m_pTypeDescr->aBase ); + } + + (*m_pReceiver->release)( m_pReceiver ); + m_pFactory->release(); +} + +inline void AdapterImpl::acquire() +{ + osl_atomic_increment( &m_nRef ); +} + +inline void AdapterImpl::release() +{ + bool delete_this = false; + { + MutexGuard guard( m_pFactory->m_mutex ); + if (! osl_atomic_decrement( &m_nRef )) + { + t_ptr_map::iterator iFind( + m_pFactory->m_receiver2adapters.find( m_key ) ); + assert( m_pFactory->m_receiver2adapters.end() != iFind ); + t_ptr_set & adapter_set = iFind->second; + if (adapter_set.erase( this ) != 1) { + OSL_ASSERT( false ); + } + if (adapter_set.empty()) + { + m_pFactory->m_receiver2adapters.erase( iFind ); + } + delete_this = true; + } + } + if (delete_this) + delete this; +} + + +static void constructRuntimeException( + uno_Any * pExc, const OUString & rMsg ) +{ + RuntimeException exc( rMsg ); + // no conversion needed due to binary compatibility + no convertible type + ::uno_type_any_construct( + pExc, &exc, cppu::UnoType<decltype(exc)>::get().getTypeLibType(), nullptr ); +} + + +static bool type_equals( + typelib_TypeDescriptionReference * pType1, + typelib_TypeDescriptionReference * pType2 ) +{ + return (pType1 == pType2 || + (pType1->pTypeName->length == pType2->pTypeName->length && + 0 == ::rtl_ustr_compare( + pType1->pTypeName->buffer, pType2->pTypeName->buffer ))); +} + + +bool AdapterImpl::coerce_assign( + void * pDest, typelib_TypeDescriptionReference * pType, uno_Any * pSource, + uno_Any * pOutExc ) +{ + if (typelib_TypeClass_ANY == pType->eTypeClass) + { + ::uno_type_any_assign( + static_cast<uno_Any *>(pDest), pSource->pData, pSource->pType, nullptr, nullptr ); + return true; + } + if (::uno_type_assignData( + pDest, pType, pSource->pData, pSource->pType, nullptr, nullptr, nullptr )) + { + return true; + } + else // try type converter + { + uno_Any ret; + void * args[ 2 ]; + args[ 0 ] = pSource; + args[ 1 ] = &pType; + uno_Any exc; + uno_Any * p_exc = &exc; + + // converTo() + (*m_pFactory->m_pConverter->pDispatcher)( + m_pFactory->m_pConverter, + m_pFactory->m_pConvertToTD, &ret, args, &p_exc ); + + if (p_exc) // exception occurred + { + OSL_ASSERT( + p_exc->pType->eTypeClass == typelib_TypeClass_EXCEPTION ); + if (typelib_typedescriptionreference_isAssignableFrom( cppu::UnoType<RuntimeException>::get().getTypeLibType(), + p_exc->pType )) + { + // is RuntimeException or derived: rethrow + uno_type_any_construct( + pOutExc, p_exc->pData, p_exc->pType, nullptr ); + } + else + { + // set runtime exception + constructRuntimeException( + pOutExc, "type coercion failed: " + + static_cast< Exception const * >( + p_exc->pData )->Message ); + } + ::uno_any_destruct( p_exc, nullptr ); + // pOutExc constructed + return false; + } + else + { + bool succ = ::uno_type_assignData( + pDest, pType, ret.pData, ret.pType, nullptr, nullptr, nullptr ); + ::uno_any_destruct( &ret, nullptr ); + OSL_ENSURE( + succ, "### conversion succeeded, but assignment failed!?" ); + if (! succ) + { + // set runtime exception + constructRuntimeException( + pOutExc, + "type coercion failed: " + "conversion succeeded, but assignment failed?!" ); + } + return succ; + } + } +} + +inline bool AdapterImpl::coerce_construct( + void * pDest, typelib_TypeDescriptionReference * pType, uno_Any * pSource, + uno_Any * pExc ) +{ + if (typelib_TypeClass_ANY == pType->eTypeClass) + { + ::uno_type_copyData( pDest, pSource, pType, nullptr ); + return true; + } + if (type_equals( pType, pSource->pType)) + { + ::uno_type_copyData( pDest, pSource->pData, pType, nullptr ); + return true; + } + ::uno_type_constructData( pDest, pType ); + return coerce_assign( pDest, pType, pSource, pExc ); +} + + +static void handleInvokExc( uno_Any * pDest, uno_Any * pSource ) +{ + OUString const & name = + OUString::unacquired( &pSource->pType->pTypeName ); + + if ( name == "com.sun.star.reflection.InvocationTargetException" ) + { + // unwrap invocation target exception + uno_Any * target_exc = + &static_cast< reflection::InvocationTargetException * >( + pSource->pData )->TargetException; + ::uno_type_any_construct( + pDest, target_exc->pData, target_exc->pType, nullptr ); + } + else // all other exceptions are wrapped to RuntimeException + { + if (typelib_TypeClass_EXCEPTION == pSource->pType->eTypeClass) + { + constructRuntimeException( + pDest, static_cast<Exception const *>(pSource->pData)->Message ); + } + else + { + constructRuntimeException( + pDest, "no exception has been thrown via invocation?!" ); + } + } +} + +void AdapterImpl::getValue( + const typelib_TypeDescription * pMemberType, + void * pReturn, uno_Any ** ppException ) +{ + uno_Any aInvokRet; + void * pInvokArgs[1]; + pInvokArgs[0] = const_cast<rtl_uString **>( + &reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->pMemberName); + uno_Any aInvokExc; + uno_Any * pInvokExc = &aInvokExc; + + // getValue() + (*m_pReceiver->pDispatcher)( + m_pReceiver, m_pFactory->m_pGetValueTD, + &aInvokRet, pInvokArgs, &pInvokExc ); + + if (pInvokExc) // getValue() call exception + { + handleInvokExc( *ppException, pInvokExc ); + ::uno_any_destruct( pInvokExc, nullptr ); // cleanup + } + else // invocation call succeeded + { + if (coerce_construct( + pReturn, + reinterpret_cast<typelib_InterfaceAttributeTypeDescription const *>( + pMemberType)->pAttributeTypeRef, + &aInvokRet, *ppException )) + { + *ppException = nullptr; // no exceptions be thrown + } + ::uno_any_destruct( &aInvokRet, nullptr ); + } +} + +void AdapterImpl::setValue( + const typelib_TypeDescription * pMemberType, + void * pArgs[], uno_Any ** ppException ) +{ + uno_Any aInvokVal; + ::uno_type_any_construct( + &aInvokVal, pArgs[0], + reinterpret_cast<typelib_InterfaceAttributeTypeDescription const *>( + pMemberType)->pAttributeTypeRef, nullptr ); + + void * pInvokArgs[2]; + pInvokArgs[0] = const_cast<rtl_uString **>( + &reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->pMemberName); + pInvokArgs[1] = &aInvokVal; + uno_Any aInvokExc; + uno_Any * pInvokExc = &aInvokExc; + + // setValue() + (*m_pReceiver->pDispatcher)( + m_pReceiver, m_pFactory->m_pSetValueTD, nullptr, pInvokArgs, &pInvokExc ); + + if (pInvokExc) // setValue() call exception + { + handleInvokExc( *ppException, pInvokExc ); + ::uno_any_destruct( pInvokExc, nullptr ); // cleanup + } + else // invocation call succeeded + { + *ppException = nullptr; // no exceptions be thrown + } + + ::uno_any_destruct( &aInvokVal, nullptr ); // cleanup +} + +void AdapterImpl::invoke( + const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs[], uno_Any ** ppException ) +{ + sal_Int32 nParams = + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberType)->nParams; + typelib_MethodParameter * pFormalParams = + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberType)->pParams; + + // in params + uno_Sequence * pInParamsSeq = nullptr; + ::uno_sequence_construct( + &pInParamsSeq, m_pFactory->m_pAnySeqTD, nullptr, nParams, nullptr ); + uno_Any * pInAnys = reinterpret_cast<uno_Any *>(pInParamsSeq->elements); + sal_Int32 nOutParams = 0; + sal_Int32 nPos; + for ( nPos = nParams; nPos--; ) + { + typelib_MethodParameter const & rParam = pFormalParams[nPos]; + if (rParam.bIn) // is in/inout param + { + ::uno_type_any_assign( + &pInAnys[nPos], pArgs[nPos], rParam.pTypeRef, nullptr, nullptr ); + } + // else: pure out is empty any + + if (rParam.bOut) + ++nOutParams; + } + + // out params, out indices + uno_Sequence * pOutIndices; + uno_Sequence * pOutParams; + // return value + uno_Any aInvokRet; + // perform call + void * pInvokArgs[4]; + pInvokArgs[0] = const_cast<rtl_uString **>( + &reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->pMemberName); + pInvokArgs[1] = &pInParamsSeq; + pInvokArgs[2] = &pOutIndices; + pInvokArgs[3] = &pOutParams; + uno_Any aInvokExc; + uno_Any * pInvokExc = &aInvokExc; + + // invoke() call + (*m_pReceiver->pDispatcher)( + m_pReceiver, m_pFactory->m_pInvokMethodTD, + &aInvokRet, pInvokArgs, &pInvokExc ); + + if (pInvokExc) + { + handleInvokExc( *ppException, pInvokExc ); + ::uno_any_destruct( pInvokExc, nullptr ); // cleanup + } + else // no invocation exception + { + // write changed out params + OSL_ENSURE( + pOutParams->nElements == nOutParams && + pOutIndices->nElements == nOutParams, + "### out params lens differ!" ); + if (pOutParams->nElements == nOutParams && + pOutIndices->nElements == nOutParams) + { + sal_Int16 * pIndices = reinterpret_cast<sal_Int16 *>(pOutIndices->elements); + uno_Any * pOut = reinterpret_cast<uno_Any *>(pOutParams->elements); + for ( nPos = 0; nPos < nOutParams; ++nPos ) + { + sal_Int32 nIndex = pIndices[nPos]; + OSL_ENSURE( nIndex < nParams, "### illegal index!" ); + typelib_MethodParameter const & rParam = pFormalParams[nIndex]; + bool succ; + if (rParam.bIn) // is in/inout param + { + succ = coerce_assign( + pArgs[nIndex], rParam.pTypeRef, &pOut[nPos], + *ppException ); + } + else // pure out + { + succ = coerce_construct( + pArgs[nIndex], rParam.pTypeRef, &pOut[nPos], + *ppException ); + } + if (! succ) // cleanup of out params + { + for ( sal_Int32 n = 0; n <= nPos; ++n ) + { + sal_Int32 nIndex2 = pIndices[n]; + OSL_ENSURE( nIndex2 < nParams, "### illegal index!" ); + typelib_MethodParameter const & rParam2 = + pFormalParams[nIndex2]; + if (! rParam2.bIn) // is pure out param + { + ::uno_type_destructData( + pArgs[nIndex2], rParam2.pTypeRef, nullptr ); + } + } + } + } + if (nPos == pOutIndices->nElements) + { + // out param copy ok; write return value + if (coerce_construct( + pReturn, + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>( + pMemberType)->pReturnTypeRef, + &aInvokRet, *ppException )) + { + *ppException = nullptr; // no exception + } + } + } + else + { + // set runtime exception + constructRuntimeException( + *ppException, + "out params lengths differ after invocation call!" ); + } + // cleanup invok out params + ::uno_destructData( &pOutIndices, m_pFactory->m_pShortSeqTD, nullptr ); + ::uno_destructData( &pOutParams, m_pFactory->m_pAnySeqTD, nullptr ); + // cleanup invok return value + ::uno_any_destruct( &aInvokRet, nullptr ); + } + // cleanup constructed in params + ::uno_destructData( &pInParamsSeq, m_pFactory->m_pAnySeqTD, nullptr ); +} + +extern "C" +{ + +static void adapter_acquire( uno_Interface * pUnoI ) +{ + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter->acquire(); +} + +static void adapter_release( uno_Interface * pUnoI ) +{ + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter->release(); +} + +static void adapter_dispatch( + uno_Interface * pUnoI, const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs[], uno_Any ** ppException ) +{ + // query to emulated interface + switch (reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->nPosition) + { + case 0: // queryInterface() + { + AdapterImpl * that = + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter; + *ppException = nullptr; // no exc + typelib_TypeDescriptionReference * pDemanded = + *static_cast<typelib_TypeDescriptionReference **>(pArgs[0]); + // pInterfaces[0] is XInterface + for ( size_t nPos = 0; nPos < that->m_vInterfaces.size(); ++nPos ) + { + typelib_InterfaceTypeDescription * pTD = + that->m_vInterfaces[nPos].m_pTypeDescr; + while (pTD) + { + if (type_equals( pTD->aBase.pWeakRef, pDemanded )) + { + uno_Interface * pUnoI2 = &that->m_vInterfaces[nPos]; + ::uno_any_construct( + static_cast<uno_Any *>(pReturn), &pUnoI2, + &pTD->aBase, nullptr ); + return; + } + pTD = pTD->pBaseTypeDescription; + } + } + ::uno_any_construct( static_cast<uno_Any *>(pReturn), nullptr, nullptr, nullptr ); // clear() + break; + } + case 1: // acquire() + *ppException = nullptr; // no exc + adapter_acquire( pUnoI ); + break; + case 2: // release() + *ppException = nullptr; // no exc + adapter_release( pUnoI ); + break; + + default: + { + AdapterImpl * that = + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter; + if (pMemberType->eTypeClass == typelib_TypeClass_INTERFACE_METHOD) + { + that->invoke( pMemberType, pReturn, pArgs, ppException ); + } + else // attribute + { + if (pReturn) + that->getValue( pMemberType, pReturn, ppException ); + else + that->setValue( pMemberType, pArgs, ppException ); + } + } + } +} +} + +AdapterImpl::AdapterImpl( + void * key, Reference< script::XInvocation > const & xReceiver, + const Sequence< Type > & rTypes, + FactoryImpl * pFactory ) + : m_nRef( 1 ), + m_pFactory( pFactory ), + m_key( key ), + m_vInterfaces( rTypes.getLength() ) +{ + // init adapters + const Type * pTypes = rTypes.getConstArray(); + for ( sal_Int32 nPos = rTypes.getLength(); nPos--; ) + { + InterfaceAdapterImpl * pInterface = &m_vInterfaces[nPos]; + pInterface->acquire = adapter_acquire; + pInterface->release = adapter_release; + pInterface->pDispatcher = adapter_dispatch; + pInterface->m_pAdapter = this; + pInterface->m_pTypeDescr = nullptr; + pTypes[nPos].getDescription( + reinterpret_cast<typelib_TypeDescription **>(&pInterface->m_pTypeDescr) ); + OSL_ASSERT( pInterface->m_pTypeDescr ); + if (! pInterface->m_pTypeDescr) + { + for ( sal_Int32 n = 0; n < nPos; ++n ) + { + ::typelib_typedescription_release( + &m_vInterfaces[ n ].m_pTypeDescr->aBase ); + } + throw RuntimeException( + "cannot retrieve all interface type infos!" ); + } + } + + // map receiver + m_pReceiver = static_cast<uno_Interface *>(m_pFactory->m_aCpp2Uno.mapInterface( + xReceiver.get(), cppu::UnoType<decltype(xReceiver)>::get() )); + OSL_ASSERT( nullptr != m_pReceiver ); + if (! m_pReceiver) + { + throw RuntimeException( "cannot map receiver!" ); + } + + m_pFactory->acquire(); +} + + +FactoryImpl::FactoryImpl( Reference< XComponentContext > const & xContext ) + : m_aUno2Cpp(Mapping( UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME )), + m_aCpp2Uno(Mapping( CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO)), + m_pInvokMethodTD( nullptr ), + m_pSetValueTD( nullptr ), + m_pGetValueTD( nullptr ), + m_pAnySeqTD( nullptr ), + m_pShortSeqTD( nullptr ), + m_pConvertToTD( nullptr ) +{ + // C++/UNO bridge + OSL_ENSURE( + m_aUno2Cpp.is() && m_aCpp2Uno.is(), "### no uno / C++ mappings!" ); + + // type converter + Reference< script::XTypeConverter > xConverter( + xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.script.Converter", + xContext ), + UNO_QUERY_THROW ); + m_pConverter = static_cast<uno_Interface *>(m_aCpp2Uno.mapInterface( + xConverter.get(), cppu::UnoType<decltype(xConverter)>::get() )); + OSL_ASSERT( nullptr != m_pConverter ); + + // some type info: + // sequence< any > + Type const & rAnySeqType = cppu::UnoType<Sequence< Any >>::get(); + rAnySeqType.getDescription( &m_pAnySeqTD ); + // sequence< short > + const Type & rShortSeqType = + cppu::UnoType<Sequence< sal_Int16 >>::get(); + rShortSeqType.getDescription( &m_pShortSeqTD ); + // script.XInvocation + typelib_TypeDescription * pTD = nullptr; + const Type & rInvType = cppu::UnoType<script::XInvocation>::get(); + TYPELIB_DANGER_GET( &pTD, rInvType.getTypeLibType() ); + typelib_InterfaceTypeDescription * pITD; + pITD = reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD); + if( ! pITD->aBase.bComplete ) + typelib_typedescription_complete( &pTD ); + ::typelib_typedescriptionreference_getDescription( + &m_pInvokMethodTD, pITD->ppMembers[ 1 ] ); // invoke() + ::typelib_typedescriptionreference_getDescription( + &m_pSetValueTD, pITD->ppMembers[ 2 ] ); // setValue() + ::typelib_typedescriptionreference_getDescription( + &m_pGetValueTD, pITD->ppMembers[ 3 ] ); // getValue() + // script.XTypeConverter + const Type & rTCType = + cppu::UnoType<script::XTypeConverter>::get(); + TYPELIB_DANGER_GET( &pTD, rTCType.getTypeLibType() ); + pITD = reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD); + ::typelib_typedescriptionreference_getDescription( + &m_pConvertToTD, pITD->ppMembers[ 0 ] ); // convertTo() + TYPELIB_DANGER_RELEASE( pTD ); + + if (!m_pInvokMethodTD || !m_pSetValueTD || !m_pGetValueTD || + !m_pConvertToTD || + !m_pAnySeqTD || !m_pShortSeqTD) + { + throw RuntimeException( "missing type descriptions!" ); + } +} + +FactoryImpl::~FactoryImpl() +{ + ::typelib_typedescription_release( m_pInvokMethodTD ); + ::typelib_typedescription_release( m_pSetValueTD ); + ::typelib_typedescription_release( m_pGetValueTD ); + ::typelib_typedescription_release( m_pAnySeqTD ); + ::typelib_typedescription_release( m_pShortSeqTD ); + ::typelib_typedescription_release( m_pConvertToTD ); + + (*m_pConverter->release)( m_pConverter ); + +#if OSL_DEBUG_LEVEL > 0 + assert(m_receiver2adapters.empty() && "still adapters out there!?"); +#endif +} + + +static AdapterImpl * lookup_adapter( + t_ptr_set ** pp_adapter_set, + t_ptr_map & map, void * key, Sequence< Type > const & rTypes ) +{ + t_ptr_set & adapters_set = map[ key ]; + *pp_adapter_set = &adapters_set; + if (adapters_set.empty()) + return nullptr; // shortcut + // find matching adapter + Type const * pTypes = rTypes.getConstArray(); + sal_Int32 nTypes = rTypes.getLength(); + for (const auto& rpAdapter : adapters_set) + { + AdapterImpl * that = static_cast< AdapterImpl * >( rpAdapter ); + // iterate through all types if that is a matching adapter + sal_Int32 nPosTypes; + for ( nPosTypes = nTypes; nPosTypes--; ) + { + Type const & rType = pTypes[ nPosTypes ]; + // find in adapter's type list + sal_Int32 nPos; + for ( nPos = that->m_vInterfaces.size(); nPos--; ) + { + if (::typelib_typedescriptionreference_isAssignableFrom( + rType.getTypeLibType(), + that->m_vInterfaces[ nPos ].m_pTypeDescr->aBase.pWeakRef )) + { + // found + break; + } + } + if (nPos < 0) // type not found => next adapter + break; + } + if (nPosTypes < 0) // all types found + return that; + } + return nullptr; +} + +// XInvocationAdapterFactory2 impl + +Reference< XInterface > FactoryImpl::createAdapter( + const Reference< script::XInvocation > & xReceiver, + const Sequence< Type > & rTypes ) +{ + Reference< XInterface > xRet; + if (xReceiver.is() && rTypes.hasElements()) + { + t_ptr_set * adapter_set; + AdapterImpl * that; + Reference< XInterface > xKey( xReceiver, UNO_QUERY ); + { + ClearableMutexGuard guard( m_mutex ); + that = lookup_adapter( + &adapter_set, m_receiver2adapters, xKey.get(), rTypes ); + if (nullptr == that) // no entry + { + guard.clear(); + // create adapter; already acquired: m_nRef == 1 + AdapterImpl * pNew = + new AdapterImpl( xKey.get(), xReceiver, rTypes, this ); + // lookup again + ClearableMutexGuard guard2( m_mutex ); + that = lookup_adapter( + &adapter_set, m_receiver2adapters, xKey.get(), rTypes ); + if (nullptr == that) // again no entry + { + std::pair< t_ptr_set::const_iterator, bool > i(adapter_set->insert(pNew)); + SAL_WARN_IF( + !i.second, "stoc", + "set already contains " << *(i.first) << " != " << pNew); + that = pNew; + } + else + { + that->acquire(); + guard2.clear(); + delete pNew; // has never been inserted + } + } + else // found adapter + { + that->acquire(); + } + } + // map one interface to C++ + uno_Interface * pUnoI = that->m_vInterfaces.data(); + m_aUno2Cpp.mapInterface( + reinterpret_cast<void **>(&xRet), pUnoI, cppu::UnoType<decltype(xRet)>::get() ); + that->release(); + OSL_ASSERT( xRet.is() ); + if (! xRet.is()) + { + throw RuntimeException( "mapping UNO to C++ failed!" ); + } + } + return xRet; +} +// XInvocationAdapterFactory impl + +Reference< XInterface > FactoryImpl::createAdapter( + const Reference< script::XInvocation > & xReceiver, const Type & rType ) +{ + return createAdapter( xReceiver, Sequence< Type >( &rType, 1 ) ); +} + +// XServiceInfo + +OUString FactoryImpl::getImplementationName() +{ + return "com.sun.star.comp.stoc.InvocationAdapterFactory"; +} + +sal_Bool FactoryImpl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > FactoryImpl::getSupportedServiceNames() +{ + return { "com.sun.star.script.InvocationAdapterFactory" }; +} + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_invocation_adapter_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new stoc_invadp::FactoryImpl(context)); +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/invocation_adapterfactory/invocadapt.component b/stoc/source/invocation_adapterfactory/invocadapt.component new file mode 100644 index 0000000000..ec1723e22a --- /dev/null +++ b/stoc/source/invocation_adapterfactory/invocadapt.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.InvocationAdapterFactory" + constructor="stoc_invocation_adapter_get_implementation" single-instance="true"> + <service name="com.sun.star.script.InvocationAdapterFactory"/> + </implementation> +</component> diff --git a/stoc/source/javaloader/javaloader.component b/stoc/source/javaloader/javaloader.component new file mode 100644 index 0000000000..e8d1533bac --- /dev/null +++ b/stoc/source/javaloader/javaloader.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.JavaComponentLoader" + constructor="stoc_JavaComponentLoader_get_implementation" + single-instance="true"> + <service name="com.sun.star.loader.Java"/> + <service name="com.sun.star.loader.Java2"/> + </implementation> +</component> diff --git a/stoc/source/javaloader/javaloader.cxx b/stoc/source/javaloader/javaloader.cxx new file mode 100644 index 0000000000..e54ce9b2d7 --- /dev/null +++ b/stoc/source/javaloader/javaloader.cxx @@ -0,0 +1,574 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rtl/process.h> +#include <sal/log.hxx> + +#include <uno/environment.h> +#include <uno/lbnames.h> +#include <uno/mapping.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#ifdef LINUX +#undef minor +#undef major +#endif + +#include <com/sun/star/java/XJavaVM.hpp> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <rtl/random.h> +#include <rtl/ustrbuf.hxx> +#include <osl/security.hxx> + +#include <cppuhelper/factory.hxx> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/bridge/UnoUrlResolver.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> + +#include <jvmaccess/unovirtualmachine.hxx> +#include <jvmaccess/virtualmachine.hxx> + +// this one is header-only +#include <comphelper/sequence.hxx> + +#include <mutex> +#include <thread> +#include <utility> + +namespace com::sun::star::registry { class XRegistryKey; } + +using namespace css::java; +using namespace css::lang; +using namespace css::loader; +using namespace css::uno; +using namespace css::registry; + +using namespace ::cppu; +using namespace ::osl; + +namespace stoc_javaloader { + +namespace { + +// from desktop/source/deployment/misc/dp_misc.cxx +OUString generateRandomPipeId() +{ + // compute some good pipe id: + static rtlRandomPool s_hPool = rtl_random_createPool(); + if (s_hPool == nullptr) + throw RuntimeException( "cannot create random pool!?", nullptr ); + sal_uInt8 bytes[ 32 ]; + if (rtl_random_getBytes( + s_hPool, bytes, SAL_N_ELEMENTS(bytes) ) != rtl_Random_E_None) { + throw RuntimeException( "random pool error!?", nullptr ); + } + OUStringBuffer buf; + for (unsigned char byte : bytes) { + buf.append( static_cast<sal_Int32>(byte), 0x10 ); + } + return buf.makeStringAndClear(); +} + +// from desktop/source/deployment/registry/component/dp_component.cxx +/** return a vector of bootstrap variables which have been provided + as command arguments. +*/ +std::vector<OUString> getCmdBootstrapVariables() +{ + std::vector<OUString> ret; + sal_uInt32 count = osl_getCommandArgCount(); + for (sal_uInt32 i = 0; i < count; i++) + { + OUString arg; + osl_getCommandArg(i, &arg.pData); + if (arg.startsWith("-env:")) + ret.push_back(arg); + } + return ret; +} + +// from desktop/source/deployment/misc/dp_misc.cxx +oslProcess raiseProcess( + OUString const & appURL, Sequence<OUString> const & args ) +{ + ::osl::Security sec; + oslProcess hProcess = nullptr; + oslProcessError rc = osl_executeProcess( + appURL.pData, + reinterpret_cast<rtl_uString **>( + const_cast<OUString *>(args.getConstArray()) ), + args.getLength(), + osl_Process_DETACHED, + sec.getHandle(), + nullptr, // => current working dir + nullptr, 0, // => no env vars + &hProcess ); + + switch (rc) { + case osl_Process_E_None: + break; + case osl_Process_E_NotFound: + throw RuntimeException( "image not found!", nullptr ); + case osl_Process_E_TimedOut: + throw RuntimeException( "timeout occurred!", nullptr ); + case osl_Process_E_NoPermission: + throw RuntimeException( "permission denied!", nullptr ); + case osl_Process_E_Unknown: + throw RuntimeException( "unknown error!", nullptr ); + case osl_Process_E_InvalidError: + default: + throw RuntimeException( "unmapped error!", nullptr ); + } + + return hProcess; +} + +// from desktop/source/deployment/registry/component/dp_component.cxx +Reference<XComponentContext> raise_uno_process( + Reference<XComponentContext> const & xContext) +{ + OSL_ASSERT( xContext.is() ); + + OUString const url(css::util::theMacroExpander::get(xContext)->expandMacros("$URE_BIN_DIR/uno")); + + const OUString connectStr = "uno:pipe,name=" + generateRandomPipeId() + ";urp;uno.ComponentContext"; + + // raise core UNO process to register/run a component, + // javavm service uses unorc next to executable to retrieve deployed + // jar typelibs + + std::vector<OUString> args{ +#if OSL_DEBUG_LEVEL == 0 + "--quiet", +#endif + "--singleaccept", + "-u", + connectStr, + // don't inherit from unorc: + "-env:INIFILENAME=" }; + + //now add the bootstrap variables which were supplied on the command line + std::vector<OUString> bootvars = getCmdBootstrapVariables(); + args.insert(args.end(), bootvars.begin(), bootvars.end()); + + oslProcess hProcess; + try { + hProcess = raiseProcess(url, comphelper::containerToSequence(args)); + } + catch (...) { + OUStringBuffer sMsg = "error starting process: " + url; + for (const auto& arg : args) { + sMsg.append(" " + arg); + } + throw css::uno::RuntimeException(sMsg.makeStringAndClear()); + } + try { + // from desktop/source/deployment/misc/dp_misc.cxx + Reference<css::bridge::XUnoUrlResolver> const xUnoUrlResolver( + css::bridge::UnoUrlResolver::create(xContext) ); + + for (int i = 0; i <= 40; ++i) // 20 seconds + { + try { + return Reference<XComponentContext>( + xUnoUrlResolver->resolve(connectStr), + UNO_QUERY_THROW ); + } + catch (const css::connection::NoConnectException &) { + if (i < 40) { + std::this_thread::sleep_for( std::chrono::milliseconds(500) ); + } + else throw; + } + } + return nullptr; // warning C4715 + } + catch (...) { + // try to terminate process: + if ( osl_terminateProcess( hProcess ) != osl_Process_E_None ) + { + OSL_ASSERT( false ); + } + throw; + } +} + +class JavaComponentLoader + : protected ::cppu::BaseMutex + , public WeakComponentImplHelper<XImplementationLoader, XServiceInfo> +{ + /** local context */ + css::uno::Reference<XComponentContext> m_xComponentContext; + + /** possible remote process' context (use depends on configuration). + note: lifetime must be effectively "static" as this JavaComponentLoader + has no control over the lifetime of the services created via this + context; hence JavaComponentLoader is a single-instance service. + */ + css::uno::Reference<XComponentContext> m_xRemoteComponentContext; + + /** Do not use m_javaLoader directly. Instead use getJavaLoader. + This is either an in-process loader implemented in Java, + or a remote instance of JavaComponentLoader running in uno process, + acting as a proxy. + */ + css::uno::Reference<XImplementationLoader> m_javaLoader; + /** The returned Reference contains a null pointer if the office is not configured + to run java. + + @exception css::uno::RuntimeException + If the Java implementation of the loader could not be obtained, for reasons other + then that java was not configured the RuntimeException is thrown. + */ + const css::uno::Reference<XImplementationLoader> & getJavaLoader(OUString &); + + +public: + /// @throws RuntimeException + explicit JavaComponentLoader(css::uno::Reference<XComponentContext> xCtx); + +public: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + virtual void SAL_CALL disposing() override; + + // XImplementationLoader + virtual css::uno::Reference<XInterface> SAL_CALL activate( + const OUString& implementationName, const OUString& implementationLoaderUrl, + const OUString& locationUrl, const css::uno::Reference<XRegistryKey>& xKey) override; + virtual sal_Bool SAL_CALL writeRegistryInfo( + const css::uno::Reference<XRegistryKey>& xKey, + const OUString& implementationLoaderUrl, const OUString& locationUrl) override; +}; + +} + +void JavaComponentLoader::disposing() +{ + // Explicitly drop all remote refs to shut down the uno.bin process + // and particularly the connection to it, so that it can't do more calls + // during late shutdown. + m_javaLoader.clear(); + if (m_xRemoteComponentContext.is()) { + Reference<XComponent> const xComp(m_xRemoteComponentContext, UNO_QUERY); + assert(xComp.is()); + xComp->dispose(); + m_xRemoteComponentContext.clear(); + } +} + +const css::uno::Reference<XImplementationLoader> & JavaComponentLoader::getJavaLoader(OUString & rRemoteArg) +{ + static std::mutex ourMutex; + std::unique_lock aGuard(ourMutex); + + if (m_javaLoader.is()) + return m_javaLoader; + + // check if the JVM should be instantiated out-of-process + if (rRemoteArg.isEmpty()) { + if (!m_xRemoteComponentContext.is()) { + Reference<css::container::XHierarchicalNameAccess> const xConf( + m_xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.configuration.ReadOnlyAccess", + { Any(OUString("*")) }, // locale isn't relevant here + m_xComponentContext), + UNO_QUERY); + + // configmgr is not part of URE, so may not exist! + if (xConf.is()) { + Any const value(xConf->getByHierarchicalName( + "org.openoffice.Office.Java/VirtualMachine/RunUnoComponentsOutOfProcess")); + bool b; + if ((value >>= b) && b) { + SAL_INFO("stoc.java", "JavaComponentLoader: starting uno process"); + m_xRemoteComponentContext = raise_uno_process(m_xComponentContext); + } + } + } + if (m_xRemoteComponentContext.is()) { + SAL_INFO("stoc.java", "JavaComponentLoader: creating remote instance to start JVM in uno process"); + // create JVM service in remote uno.bin process + Reference<XImplementationLoader> const xLoader( + m_xRemoteComponentContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.loader.Java2", m_xRemoteComponentContext), + UNO_QUERY_THROW); + assert(xLoader.is()); + m_javaLoader = xLoader; + rRemoteArg = "remote"; + SAL_INFO("stoc.java", "JavaComponentLoader: remote proxy instance created: " << m_javaLoader.get()); + return m_javaLoader; + } + } + + uno_Environment * pJava_environment = nullptr; + uno_Environment * pUno_environment = nullptr; + typelib_InterfaceTypeDescription * pType_XImplementationLoader = nullptr; + + try { + // get a java vm, where we can create a loader + css::uno::Reference<XJavaVM> javaVM_xJavaVM( + m_xComponentContext->getValueByName( + ("/singletons/" + "com.sun.star.java.theJavaVirtualMachine")), + UNO_QUERY_THROW); + + // Use the special protocol of XJavaVM.getJavaVM: If the passed in + // process ID has an extra 17th byte of value one, the returned any + // contains a pointer to a jvmaccess::UnoVirtualMachine, instead of the + // underlying JavaVM pointer: + Sequence<sal_Int8> processID(17); + rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8 *>(processID.getArray())); + processID.getArray()[16] = 1; + + // We get a non-refcounted pointer to a jvmaccess::UnoVirtualMachine + // from the XJavaVM service (the pointer is guaranteed to be valid + // as long as our reference to the XJavaVM service lasts), and + // convert the non-refcounted pointer into a refcounted one + // immediately: + static_assert(sizeof (sal_Int64) + >= sizeof (jvmaccess::UnoVirtualMachine *), "must be at least the same size"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::UnoVirtualMachine * >(nullptr)); + javaVM_xJavaVM->getJavaVM(processID) >>= nPointer; + rtl::Reference< jvmaccess::UnoVirtualMachine > xVirtualMachine( + reinterpret_cast< jvmaccess::UnoVirtualMachine * >(nPointer)); + if (!xVirtualMachine.is()) + { + //throw RuntimeException( + // "javaloader error - JavaVirtualMachine service could not provide a VM", + // css::uno::Reference<XInterface>()); + // We must not throw a RuntimeException, because this might end the applications. + // It is ok if java components + // are not working because the office can be installed without Java support. + SAL_WARN("stoc", "getJavaVM returned null"); + return m_javaLoader; // null-ref + } + + try + { + jvmaccess::VirtualMachine::AttachGuard aGuard2( + xVirtualMachine->getVirtualMachine()); + JNIEnv * pJNIEnv = aGuard2.getEnvironment(); + + // instantiate the java JavaLoader + jclass jcClassLoader = pJNIEnv->FindClass("java/lang/ClassLoader"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not find class java/lang/ClassLoader"); + jmethodID jmLoadClass = pJNIEnv->GetMethodID( + jcClassLoader, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not find method java/lang/ClassLoader.loadClass"); + jvalue arg; + arg.l = pJNIEnv->NewStringUTF( + "com.sun.star.comp.loader.JavaLoader"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not create string"); + jclass jcJavaLoader = static_cast< jclass >( + pJNIEnv->CallObjectMethodA( + static_cast< jobject >(xVirtualMachine->getClassLoader()), + jmLoadClass, &arg)); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not find class com/sun/star/comp/loader/JavaLoader"); + jmethodID jmJavaLoader_init = pJNIEnv->GetMethodID(jcJavaLoader, "<init>", "()V"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - instantiation of com.sun.star.comp.loader.JavaLoader failed"); + jobject joJavaLoader = pJNIEnv->NewObject(jcJavaLoader, jmJavaLoader_init); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - instantiation of com.sun.star.comp.loader.JavaLoader failed"); + + // map the java JavaLoader to this environment + OUString sJava("java"); + uno_getEnvironment(&pJava_environment, sJava.pData, + xVirtualMachine.get()); + if(!pJava_environment) + throw RuntimeException( + "javaloader error - no Java environment available"); + + // why is there no convenient constructor? + OUString sCppu_current_lb_name(CPPU_CURRENT_LANGUAGE_BINDING_NAME); + uno_getEnvironment(&pUno_environment, sCppu_current_lb_name.pData, nullptr); + if(!pUno_environment) + throw RuntimeException( + "javaloader error - no C++ environment available"); + + Mapping java_curr(pJava_environment, pUno_environment); + if(!java_curr.is()) + throw RuntimeException( + "javaloader error - no mapping from java to C++ "); + + // release java environment + pJava_environment->release(pJava_environment); + pJava_environment = nullptr; + + // release uno environment + pUno_environment->release(pUno_environment); + pUno_environment = nullptr; + + cppu::UnoType<XImplementationLoader>::get(). + getDescription(reinterpret_cast<typelib_TypeDescription **>(&pType_XImplementationLoader)); + if(!pType_XImplementationLoader) + throw RuntimeException( + "javaloader error - no type information for XImplementationLoader"); + + m_javaLoader.set(static_cast<XImplementationLoader *>(java_curr.mapInterface(joJavaLoader, pType_XImplementationLoader))); + pJNIEnv->DeleteLocalRef( joJavaLoader ); + if(!m_javaLoader.is()) + throw RuntimeException( + "javaloader error - mapping of java XImplementationLoader to c++ failed"); + + typelib_typedescription_release(reinterpret_cast<typelib_TypeDescription *>(pType_XImplementationLoader)); + pType_XImplementationLoader = nullptr; + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException", + getXWeak(), anyEx ); + } + + // set the service manager at the javaloader + css::uno::Reference<XInitialization> javaLoader_XInitialization(m_javaLoader, UNO_QUERY_THROW); + + Any any; + any <<= m_xComponentContext->getServiceManager(); + + javaLoader_XInitialization->initialize(Sequence<Any>(&any, 1)); + } + catch(RuntimeException &) { + if(pJava_environment) + pJava_environment->release(pJava_environment); + + if(pUno_environment) + pUno_environment->release(pUno_environment); + + if(pType_XImplementationLoader) + typelib_typedescription_release( + reinterpret_cast<typelib_TypeDescription *>(pType_XImplementationLoader)); + throw; + } + SAL_INFO("stoc", "javaloader.cxx: mapped javaloader - 0x" << m_javaLoader.get()); + return m_javaLoader; +} + +JavaComponentLoader::JavaComponentLoader(css::uno::Reference<XComponentContext> xCtx) + : WeakComponentImplHelper(m_aMutex) + , m_xComponentContext(std::move(xCtx)) +{ + +} + +// XServiceInfo +OUString SAL_CALL JavaComponentLoader::getImplementationName() +{ + return "com.sun.star.comp.stoc.JavaComponentLoader"; +} + +sal_Bool SAL_CALL JavaComponentLoader::supportsService(const OUString & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL JavaComponentLoader::getSupportedServiceNames() +{ + return { "com.sun.star.loader.Java", "com.sun.star.loader.Java2" }; +} + + +// XImplementationLoader +sal_Bool SAL_CALL JavaComponentLoader::writeRegistryInfo( + const css::uno::Reference<XRegistryKey> & xKey, const OUString & blabla, + const OUString & rLibName) +{ + OUString remoteArg(blabla); + const css::uno::Reference<XImplementationLoader> & loader = getJavaLoader(remoteArg); + if (!loader.is()) + throw CannotRegisterImplementationException("Could not create Java implementation loader"); + return loader->writeRegistryInfo(xKey, remoteArg, rLibName); +} + +css::uno::Reference<XInterface> SAL_CALL JavaComponentLoader::activate( + const OUString & rImplName, const OUString & blabla, const OUString & rLibName, + const css::uno::Reference<XRegistryKey> & xKey) +{ + OUString remoteArg(blabla); + if (rImplName.isEmpty() && blabla.isEmpty() && rLibName.isEmpty()) + { + // preload JVM was requested + (void)getJavaLoader(remoteArg); + return css::uno::Reference<XInterface>(); + } + + const css::uno::Reference<XImplementationLoader> & loader = getJavaLoader(remoteArg); + if (!loader.is()) + throw CannotActivateFactoryException("Could not create Java implementation loader"); + return loader->activate(rImplName, remoteArg, rLibName, xKey); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_JavaComponentLoader_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + try { + return cppu::acquire(new JavaComponentLoader(context)); + } + catch(const RuntimeException & runtimeException) { + SAL_INFO( + "stoc", + "could not init javaloader due to " << runtimeException); + throw; + } +} + +} //end namespace + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/interact.cxx b/stoc/source/javavm/interact.cxx new file mode 100644 index 0000000000..49f223383a --- /dev/null +++ b/stoc/source/javavm/interact.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "interact.hxx" + +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionRetry.hpp> +#include <cppuhelper/implbase.hxx> +#include <mutex> +#include <utility> + +namespace com::sun::star::task { class XInteractionContinuation; } + +using stoc_javavm::InteractionRequest; + +namespace { + +class AbortContinuation: + public cppu::WeakImplHelper<css::task::XInteractionAbort> +{ +public: + AbortContinuation() {} + AbortContinuation(const AbortContinuation&) = delete; + AbortContinuation& operator=(const AbortContinuation&)= delete; + + virtual void SAL_CALL select() override {} + +private: + virtual ~AbortContinuation() override {} +}; + +} + +class InteractionRequest::RetryContinuation: + public cppu::WeakImplHelper<css::task::XInteractionRetry> +{ +public: + RetryContinuation(): m_bSelected(false) {} + RetryContinuation(const RetryContinuation&) = delete; + RetryContinuation& operator=(const RetryContinuation&) = delete; + + virtual void SAL_CALL select() override; + + bool isSelected() const; + +private: + virtual ~RetryContinuation() override {} + + mutable std::mutex m_aMutex; + bool m_bSelected; +}; + +void SAL_CALL InteractionRequest::RetryContinuation::select() +{ + std::scoped_lock aGuard(m_aMutex); + m_bSelected = true; +} + +bool InteractionRequest::RetryContinuation::isSelected() const +{ + std::scoped_lock aGuard(m_aMutex); + return m_bSelected; +} + +InteractionRequest::InteractionRequest(css::uno::Any aRequest): + m_aRequest(std::move(aRequest)) +{ + m_xRetryContinuation = new RetryContinuation; + m_aContinuations = { new AbortContinuation, m_xRetryContinuation }; +} + +css::uno::Any SAL_CALL InteractionRequest::getRequest() +{ + return m_aRequest; +} + +css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > +SAL_CALL InteractionRequest::getContinuations() +{ + return m_aContinuations; +} + +bool InteractionRequest::retry() const +{ + return m_xRetryContinuation.is() && m_xRetryContinuation->isSelected(); +} + +InteractionRequest::~InteractionRequest() +{} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/interact.hxx b/stoc/source/javavm/interact.hxx new file mode 100644 index 0000000000..42fa373a33 --- /dev/null +++ b/stoc/source/javavm/interact.hxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX + +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +namespace com::sun::star::task { + class XInteractionContinuation; +} + +namespace stoc_javavm { + +class InteractionRequest: + public cppu::WeakImplHelper< css::task::XInteractionRequest > +{ +public: + explicit InteractionRequest(css::uno::Any aRequest); + + virtual css::uno::Any SAL_CALL getRequest() override; + + virtual css::uno::Sequence< css::uno::Reference< + css::task::XInteractionContinuation > > SAL_CALL + getContinuations() override; + + bool retry() const; + +private: + class RetryContinuation; + + InteractionRequest(InteractionRequest const &) = delete; + void operator =(const InteractionRequest&) = delete; + + virtual ~InteractionRequest() override; + + css::uno::Any m_aRequest; + css::uno::Sequence< css::uno::Reference< + css::task::XInteractionContinuation > > m_aContinuations; + rtl::Reference< RetryContinuation > m_xRetryContinuation; +}; + +} + +#endif // INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/javavm.component b/stoc/source/javavm/javavm.component new file mode 100644 index 0000000000..71379052b8 --- /dev/null +++ b/stoc/source/javavm/javavm.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.JavaVirtualMachine" + constructor="stoc_JavaVM_get_implementation" + single-instance="true"> + <service name="com.sun.star.java.JavaVirtualMachine"/> + <singleton name="com.sun.star.java.theJavaVirtualMachine"/> + </implementation> +</component> diff --git a/stoc/source/javavm/javavm.cxx b/stoc/source/javavm/javavm.cxx new file mode 100644 index 0000000000..a1fedd1d2d --- /dev/null +++ b/stoc/source/javavm/javavm.cxx @@ -0,0 +1,1416 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "javavm.hxx" + +#include "interact.hxx" +#include "jvmargs.hxx" + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/java/JavaNotFoundException.hpp> +#include <com/sun/star/java/InvalidJavaSettingsException.hpp> +#include <com/sun/star/java/RestartRequiredException.hpp> +#include <com/sun/star/java/JavaDisabledException.hpp> +#include <com/sun/star/java/JavaVMCreationFailureException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <comphelper/propertysequence.hxx> +#include <comphelper/SetFlagContextHelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <jvmaccess/classpath.hxx> +#include <jvmaccess/unovirtualmachine.hxx> +#include <jvmaccess/virtualmachine.hxx> +#include <rtl/process.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <sal/log.hxx> +#include <uno/current_context.hxx> +#include <jvmfwk/framework.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <jni.h> + +#include <stack> +#include <string.h> +#include <time.h> +#include <memory> +#include <utility> +#include <vector> + +// Properties of the javavm can be put +// as a comma separated list in this +// environment variable +#ifdef UNIX +#define TIMEZONE "MEZ" +#else +#define TIMEZONE "MET" +#endif + +#ifdef MACOSX +#include <premac.h> +#include <CoreFoundation/CoreFoundation.h> +#include <postmac.h> +#endif + +/* Within this implementation of the com.sun.star.java.JavaVirtualMachine + * service and com.sun.star.java.theJavaVirtualMachine singleton, the method + * com.sun.star.java.XJavaVM.getJavaVM relies on the following: + * 1 The string "$URE_INTERNAL_JAVA_DIR/" is expanded via the + * com.sun.star.util.theMacroExpander singleton into an internal (see the + * com.sun.star.uri.ExternalUriReferenceTranslator service), hierarchical URI + * reference relative to which the URE JAR files can be addressed. + * 2 The string "$URE_INTERNAL_JAVA_CLASSPATH" is either not expandable via the + * com.sun.star.util.theMacroExpander singleton + * (com.sun.star.lang.IllegalArgumentException), or is expanded via the + * com.sun.star.util.theMacroExpander singleton into a list of zero or more + * internal (see the com.sun.star.uri.ExternalUriReferenceTranslator service) + * URIs, where any space characters (U+0020) are ignored (and, in particular, + * separate adjacent URIs). + * If either of these requirements is not met, getJavaVM raises a + * com.sun.star.uno.RuntimeException. + */ + +using stoc_javavm::JavaVirtualMachine; + +namespace { + + +class NoJavaIniException: public css::uno::Exception +{ +}; + +typedef std::stack< jvmaccess::VirtualMachine::AttachGuard * > GuardStack; + +extern "C" { + +static void destroyAttachGuards(void * pData) +{ + GuardStack * pStack = static_cast< GuardStack * >(pData); + if (pStack != nullptr) + { + while (!pStack->empty()) + { + delete pStack->top(); + pStack->pop(); + } + delete pStack; + } +} + +} + +bool askForRetry(css::uno::Any const & rException) +{ + if (comphelper::IsContextFlagActive("DontEnableJava")) + return false; + + css::uno::Reference< css::uno::XCurrentContext > xContext( + css::uno::getCurrentContext()); + if (xContext.is()) + { + css::uno::Reference< css::task::XInteractionHandler > xHandler; + xContext->getValueByName("java-vm.interaction-handler") + >>= xHandler; + if (xHandler.is()) + { + rtl::Reference< stoc_javavm::InteractionRequest > xRequest( + new stoc_javavm::InteractionRequest(rException)); + xHandler->handle(xRequest); + return xRequest->retry(); + } + } + return false; +} + +// Only gets the properties if the "Proxy Server" entry in the option dialog is +// set to manual (i.e. not to none) +/// @throws css::uno::Exception +void getINetPropsFromConfig(stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx ) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = xSMgr->createInstanceWithContext( + "com.sun.star.configuration.ConfigurationRegistry", + xCtx ); + if(!xConfRegistry.is()) throw css::uno::RuntimeException("javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple(xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open("org.openoffice.Inet", true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = xConfRegistry_simple->getRootKey(); + +// if ooInetProxyType is not 0 then read the settings + css::uno::Reference<css::registry::XRegistryKey> proxyEnable= xRegistryRootKey->openKey("Settings/ooInetProxyType"); + if( proxyEnable.is() && 0 != proxyEnable->getLongValue()) + { + // read http proxy name + css::uno::Reference<css::registry::XRegistryKey> httpProxy_name = xRegistryRootKey->openKey("Settings/ooInetHTTPProxyName"); + if(httpProxy_name.is() && !httpProxy_name->getStringValue().isEmpty()) { + OUString httpHost = "http.proxyHost=" + httpProxy_name->getStringValue(); + + // read http proxy port + css::uno::Reference<css::registry::XRegistryKey> httpProxy_port = xRegistryRootKey->openKey("Settings/ooInetHTTPProxyPort"); + if(httpProxy_port.is() && httpProxy_port->getLongValue()) { + OUString httpPort = "http.proxyPort=" + OUString::number(httpProxy_port->getLongValue()); + + pjvm->pushProp(httpHost); + pjvm->pushProp(httpPort); + } + } + + // read https proxy name + css::uno::Reference<css::registry::XRegistryKey> httpsProxy_name = xRegistryRootKey->openKey("Settings/ooInetHTTPSProxyName"); + if(httpsProxy_name.is() && !httpsProxy_name->getStringValue().isEmpty()) { + OUString httpsHost = "https.proxyHost=" + httpsProxy_name->getStringValue(); + + // read https proxy port + css::uno::Reference<css::registry::XRegistryKey> httpsProxy_port = xRegistryRootKey->openKey("Settings/ooInetHTTPSProxyPort"); + if(httpsProxy_port.is() && httpsProxy_port->getLongValue()) { + OUString httpsPort = "https.proxyPort=" + OUString::number(httpsProxy_port->getLongValue()); + + pjvm->pushProp(httpsHost); + pjvm->pushProp(httpsPort); + } + } + + // read nonProxyHosts + css::uno::Reference<css::registry::XRegistryKey> nonProxies_name = xRegistryRootKey->openKey("Settings/ooInetNoProxy"); + if(nonProxies_name.is() && !nonProxies_name->getStringValue().isEmpty()) { + OUString value = nonProxies_name->getStringValue(); + // replace the separator ";" by "|" + value = value.replace(';', '|'); + + OUString httpNonProxyHosts = "http.nonProxyHosts=" + value; + + pjvm->pushProp(httpNonProxyHosts); + } + } + xConfRegistry_simple->close(); +} + +/// @throws css::uno::Exception +void getDefaultLocaleFromConfig( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx ) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = + xSMgr->createInstanceWithContext( "com.sun.star.configuration.ConfigurationRegistry", xCtx ); + if(!xConfRegistry.is()) + throw css::uno::RuntimeException( + "javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple( + xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open("org.openoffice.Setup", true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = xConfRegistry_simple->getRootKey(); + + // Since 1.7 Java knows DISPLAY and FORMAT locales, which match our UI and + // system locale. See + // http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/569b1b644416/src/share/classes/java/util/Locale.java + // https://docs.oracle.com/javase/tutorial/i18n/locale/scope.html + // https://docs.oracle.com/javase/7/docs/api/java/util/Locale.html + + // Read UI language/locale. + css::uno::Reference<css::registry::XRegistryKey> xUILocale = xRegistryRootKey->openKey("L10N/ooLocale"); + if(xUILocale.is() && !xUILocale->getStringValue().isEmpty()) { + LanguageTag aLanguageTag( xUILocale->getStringValue()); + OUString language; + OUString script; + OUString country; + // Java knows nothing but plain old ISO codes, unless Locale.Builder or + // Locale.forLanguageTag() are used, or non-standardized variant field + // content which we ignore. + aLanguageTag.getIsoLanguageScriptCountry( language, script, country); + + if(!language.isEmpty()) { + OUString prop = "user.language=" + language; + pjvm->pushProp(prop); + } + + // As of Java 7 also script is supported. + if(!script.isEmpty()) { + OUString prop = "user.script=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country=" + country; + pjvm->pushProp(prop); + } + + // Java 7 DISPLAY category is our UI language/locale. + if(!language.isEmpty()) { + OUString prop = "user.language.display=" + language; + pjvm->pushProp(prop); + } + + if(!script.isEmpty()) { + OUString prop = "user.script.display=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country.display=" + country; + pjvm->pushProp(prop); + } + } + + // Read system locale. + css::uno::Reference<css::registry::XRegistryKey> xLocale = xRegistryRootKey->openKey("L10N/ooSetupSystemLocale"); + if(xLocale.is() && !xLocale->getStringValue().isEmpty()) { + LanguageTag aLanguageTag( xLocale->getStringValue()); + OUString language; + OUString script; + OUString country; + // Java knows nothing but plain old ISO codes, unless Locale.Builder or + // Locale.forLanguageTag() are used, or non-standardized variant field + // content which we ignore. + aLanguageTag.getIsoLanguageScriptCountry( language, script, country); + + // Java 7 FORMAT category is our system locale. + if(!language.isEmpty()) { + OUString prop = "user.language.format=" + language; + pjvm->pushProp(prop); + } + + if(!script.isEmpty()) { + OUString prop = "user.script.format=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country.format=" + country; + pjvm->pushProp(prop); + } + } + + xConfRegistry_simple->close(); +} + +/// @throws css::uno::Exception +void getJavaPropsFromSafetySettings( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = + xSMgr->createInstanceWithContext( + "com.sun.star.configuration.ConfigurationRegistry", + xCtx); + if(!xConfRegistry.is()) + throw css::uno::RuntimeException( + "javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple( + xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open( + "org.openoffice.Office.Java", + true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = + xConfRegistry_simple->getRootKey(); + + if (xRegistryRootKey.is()) + { + css::uno::Reference<css::registry::XRegistryKey> key_NetAccess= xRegistryRootKey->openKey("VirtualMachine/NetAccess"); + if (key_NetAccess.is()) + { + sal_Int32 val= key_NetAccess->getLongValue(); + OUString sVal; + switch( val) + { + case 0: sVal = "host"; + break; + case 1: sVal = "unrestricted"; + break; + case 3: sVal = "none"; + break; + } + OUString sProperty = "appletviewer.security.mode=" + sVal; + pjvm->pushProp(sProperty); + } + css::uno::Reference<css::registry::XRegistryKey> key_CheckSecurity= xRegistryRootKey->openKey( + "VirtualMachine/Security"); + if( key_CheckSecurity.is()) + { + bool val = static_cast<bool>(key_CheckSecurity->getLongValue()); + OUString sProperty("stardiv.security.disableSecurity="); + if( val) + sProperty += "false"; + else + sProperty += "true"; + pjvm->pushProp( sProperty); + } + } + xConfRegistry_simple->close(); +} + +void setTimeZone(stoc_javavm::JVM * pjvm) noexcept { + /* A Bug in the Java function + ** struct Hjava_util_Properties * java_lang_System_initProperties( + ** struct Hjava_lang_System *this, + ** struct Hjava_util_Properties *props); + ** This function doesn't detect MEZ, MET or "W. Europe Standard Time" + */ + struct tm *tmData; + time_t clock = time(nullptr); + tzset(); + tmData = localtime(&clock); +#ifdef MACOSX + char * p = tmData->tm_zone; +#elif defined(_MSC_VER) + char * p = _tzname[0]; + (void)tmData; +#else + char * p = tzname[0]; + (void)tmData; +#endif + + if (!strcmp(TIMEZONE, p)) + pjvm->pushProp("user.timezone=ECT"); +} + +/// @throws css::uno::Exception +void initVMConfiguration( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext > &xCtx) +{ + stoc_javavm::JVM jvm; + try { + getINetPropsFromConfig(&jvm, xSMgr, xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "can not get INETProps because of " << exception); + } + + try { + getDefaultLocaleFromConfig(&jvm, xSMgr,xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "can not get locale because of " << exception); + } + + try + { + getJavaPropsFromSafetySettings(&jvm, xSMgr, xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "couldn't get safety settings because of " << exception); + } + + *pjvm= jvm; + + // rhbz#1285356, native look will be gtk2, which crashes + // when gtk3 is already loaded. Until there is a solution + // java-side force look and feel to something that doesn't + // crash when we are using gtk3 + if (getenv("STOC_FORCE_SYSTEM_LAF")) + pjvm->pushProp("swing.systemlaf=javax.swing.plaf.metal.MetalLookAndFeel"); + + setTimeZone(pjvm); +} + +class DetachCurrentThread { +public: + explicit DetachCurrentThread(JavaVM * jvm): m_jvm(jvm) {} + + ~DetachCurrentThread() { +#ifdef MACOSX + // tdf#101376 don't detach thread if it is the main thread on macOS + // On macOS, many AWT classes do their work on the main thread + // deep in native methods in the java.awt.* classes. The problem + // is that Oracle's and OpenJDK's JVMs don't bracket their + // "perform on main thread" native calls with "attach/detach + // current thread" calls to the JVM. + if (CFRunLoopGetCurrent() != CFRunLoopGetMain()) +#endif + if (m_jvm->DetachCurrentThread() != 0) { + OSL_ASSERT(false); + } + } + + DetachCurrentThread(const DetachCurrentThread&) = delete; + DetachCurrentThread& operator=(const DetachCurrentThread&) = delete; + +private: + JavaVM * m_jvm; +}; + +} + +JavaVirtualMachine::JavaVirtualMachine( + css::uno::Reference< css::uno::XComponentContext > xContext): + WeakComponentImplHelper(m_aMutex), + m_xContext(std::move(xContext)), + m_bDisposed(false), + m_pJavaVm(nullptr), + m_aAttachGuards(destroyAttachGuards) // TODO check for validity +{} + +void SAL_CALL +JavaVirtualMachine::initialize(css::uno::Sequence< css::uno::Any > const & + rArguments) +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + if (m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "bad call to initialize", + getXWeak()); + css::beans::NamedValue val; + if (rArguments.getLength() == 1 && (rArguments[0] >>= val) && val.Name == "UnoVirtualMachine" ) + { + OSL_ENSURE( + sizeof (sal_Int64) >= sizeof (jvmaccess::UnoVirtualMachine *), + "Pointer cannot be represented as sal_Int64"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::UnoVirtualMachine * >(nullptr)); + val.Value >>= nPointer; + m_xUnoVirtualMachine = + reinterpret_cast< jvmaccess::UnoVirtualMachine * >(nPointer); + } else { + OSL_ENSURE( + sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *), + "Pointer cannot be represented as sal_Int64"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::VirtualMachine * >(nullptr)); + if (rArguments.getLength() == 1) + rArguments[0] >>= nPointer; + rtl::Reference< jvmaccess::VirtualMachine > vm( + reinterpret_cast< jvmaccess::VirtualMachine * >(nPointer)); + if (vm.is()) { + try { + m_xUnoVirtualMachine = new jvmaccess::UnoVirtualMachine(vm, nullptr); + } catch (jvmaccess::UnoVirtualMachine::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::UnoVirtualMachine::CreationException", + getXWeak(), anyEx ); + } + } + } + if (!m_xUnoVirtualMachine.is()) { + throw css::lang::IllegalArgumentException( + "sequence of exactly one any containing either (a) a" + " com.sun.star.beans.NamedValue with Name" + " \"UnoVirtualMachine\" and Value a hyper representing a" + " non-null pointer to a jvmaccess:UnoVirtualMachine, or (b)" + " a hyper representing a non-null pointer to a" + " jvmaccess::VirtualMachine required", + getXWeak(), 0); + } + m_xVirtualMachine = m_xUnoVirtualMachine->getVirtualMachine(); +} + +OUString SAL_CALL JavaVirtualMachine::getImplementationName() +{ + return "com.sun.star.comp.stoc.JavaVirtualMachine"; +} + +sal_Bool SAL_CALL +JavaVirtualMachine::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL +JavaVirtualMachine::getSupportedServiceNames() +{ + return { "com.sun.star.java.JavaVirtualMachine" }; +} + +css::uno::Any SAL_CALL +JavaVirtualMachine::getJavaVM(css::uno::Sequence< sal_Int8 > const & rProcessId) +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + css::uno::Sequence< sal_Int8 > aId(16); + rtl_getGlobalProcessId(reinterpret_cast< sal_uInt8 * >(aId.getArray())); + enum ReturnType { + RETURN_JAVAVM, RETURN_VIRTUALMACHINE, RETURN_UNOVIRTUALMACHINE }; + ReturnType returnType = + rProcessId.getLength() == 17 && rProcessId[16] == 0 + ? RETURN_VIRTUALMACHINE + : rProcessId.getLength() == 17 && rProcessId[16] == 1 + ? RETURN_UNOVIRTUALMACHINE + : RETURN_JAVAVM; + css::uno::Sequence< sal_Int8 > aProcessId(rProcessId); + if (returnType != RETURN_JAVAVM) + aProcessId.realloc(16); + if (aId != aProcessId) + return css::uno::Any(); + + std::unique_ptr<JavaInfo> info; + while (!m_xVirtualMachine.is()) // retry until successful + { + stoc_javavm::JVM aJvm; + initVMConfiguration(&aJvm, m_xContext->getServiceManager(), + m_xContext); + const std::vector<OUString> & props = aJvm.getProperties(); + std::vector<OUString> options; + options.reserve(props.size()); + for (auto const& i : props) + { + options.push_back(i.startsWith("-") ? i : "-D" + i); + } + + JNIEnv * pMainThreadEnv = nullptr; + javaFrameworkError errcode; + + if (getenv("STOC_FORCE_NO_JRE")) + errcode = JFW_E_NO_SELECT; + else + errcode = jfw_startVM(info.get(), options, & m_pJavaVm, + & pMainThreadEnv); + + bool bStarted = false; + switch (errcode) + { + case JFW_E_NONE: bStarted = true; break; + case JFW_E_NO_SELECT: + { + // No Java configured. We silently run the Java configuration + info.reset(); + javaFrameworkError errFind; + if (getenv("STOC_FORCE_NO_JRE")) + errFind = JFW_E_NO_JAVA_FOUND; + else + errFind = jfw_findAndSelectJRE(&info); + if (errFind == JFW_E_NONE) + { + continue; + } + else if (errFind == JFW_E_NO_JAVA_FOUND) + { + + //Warning MessageBox: + //%PRODUCTNAME requires a Java runtime environment (JRE) to perform this task. + //Please install a JRE and restart %PRODUCTNAME. + css::java::JavaNotFoundException exc( + "JavaVirtualMachine::getJavaVM failed because" + " No suitable JRE found!", + getXWeak()); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + else + { + //An unexpected error occurred + throw css::uno::RuntimeException( + "[JavaVirtualMachine]:An unexpected error occurred" + " while searching for a Java, " + OUString::number(errFind), nullptr); + } + } + case JFW_E_INVALID_SETTINGS: + { + //Warning MessageBox: + // The %PRODUCTNAME configuration has been changed. Under Tools + // - Options - %PRODUCTNAME - Java, select the Java runtime environment + // you want to have used by %PRODUCTNAME. + css::java::InvalidJavaSettingsException exc( + "JavaVirtualMachine::getJavaVM failed because" + " Java settings have changed!", + getXWeak()); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + case JFW_E_JAVA_DISABLED: + { + //QueryBox: + //%PRODUCTNAME requires a Java runtime environment (JRE) to perform + //this task. However, use of a JRE has been disabled. Do you want to + //enable the use of a JRE now? + css::java::JavaDisabledException exc( + "JavaVirtualMachine::getJavaVM failed because Java is disabled!", + getXWeak()); + if( ! askForRetry(css::uno::Any(exc))) + return css::uno::Any(); + continue; + } + case JFW_E_VM_CREATION_FAILED: + { + //If the creation failed because the JRE has been uninstalled then + //we search another one. As long as there is a javaldx, we should + //never come into this situation. javaldx checks always if the JRE + //still exist. + std::unique_ptr<JavaInfo> aJavaInfo; + if (JFW_E_NONE == jfw_getSelectedJRE(&aJavaInfo)) + { + bool bExist = false; + if (JFW_E_NONE == jfw_existJRE(aJavaInfo.get(), &bExist)) + { + if (!bExist + && ! (aJavaInfo->nRequirements & JFW_REQUIRE_NEEDRESTART)) + { + info.reset(); + javaFrameworkError errFind = jfw_findAndSelectJRE( + &info); + if (errFind == JFW_E_NONE) + { + continue; + } + } + } + } + + //Error: %PRODUCTNAME requires a Java + //runtime environment (JRE) to perform this task. The selected JRE + //is defective. Please select another version or install a new JRE + //and select it under Tools - Options - %PRODUCTNAME - Java. + css::java::JavaVMCreationFailureException exc( + "JavaVirtualMachine::getJavaVM failed because Java is defective!", + getXWeak(), 0); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + case JFW_E_RUNNING_JVM: + { + //This service should make sure that we do not start java twice. + OSL_ASSERT(false); + break; + } + case JFW_E_NEED_RESTART: + { + //Error: + //For the selected Java runtime environment to work properly, + //%PRODUCTNAME must be restarted. Please restart %PRODUCTNAME now. + css::java::RestartRequiredException exc( + "JavaVirtualMachine::getJavaVM failed because " + "Office must be restarted before Java can be used!", + getXWeak()); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + default: + //RuntimeException: error is somewhere in the java framework. + //An unexpected error occurred + throw css::uno::RuntimeException( + "[JavaVirtualMachine]:An unexpected error occurred" + " while starting Java!", nullptr); + } + + if (bStarted) + { + { + DetachCurrentThread detach(m_pJavaVm); + // necessary to make debugging work; this thread will be + // suspended when the destructor of detach returns + m_xVirtualMachine = new jvmaccess::VirtualMachine( + m_pJavaVm, JNI_VERSION_1_2, true, pMainThreadEnv); + setUpUnoVirtualMachine(pMainThreadEnv); + } + // Listen for changes in the configuration (e.g. proxy settings): + // TODO this is done too late; changes to the configuration done + // after the above call to initVMConfiguration are lost + registerConfigChangesListener(); + + break; + } + } + if (!m_xUnoVirtualMachine.is()) { + try { + jvmaccess::VirtualMachine::AttachGuard guard(m_xVirtualMachine); + setUpUnoVirtualMachine(guard.getEnvironment()); + } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException occurred", + getXWeak(), anyEx ); + } + } + switch (returnType) { + default: // RETURN_JAVAVM + if (m_pJavaVm == nullptr) { + throw css::uno::RuntimeException( + "JavaVirtualMachine service was initialized in a way" + " that the requested JavaVM pointer is not available", + getXWeak()); + } + return css::uno::Any(reinterpret_cast< sal_IntPtr >(m_pJavaVm)); + case RETURN_VIRTUALMACHINE: + OSL_ASSERT(sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *)); + return css::uno::Any( + reinterpret_cast< sal_Int64 >( + m_xUnoVirtualMachine->getVirtualMachine().get())); + case RETURN_UNOVIRTUALMACHINE: + OSL_ASSERT(sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *)); + return css::uno::Any( + reinterpret_cast< sal_Int64 >(m_xUnoVirtualMachine.get())); + } +} + +sal_Bool SAL_CALL JavaVirtualMachine::isVMStarted() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), getXWeak()); + return m_xUnoVirtualMachine.is(); +} + +sal_Bool SAL_CALL JavaVirtualMachine::isVMEnabled() +{ + { + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), getXWeak()); + } +// stoc_javavm::JVM aJvm; +// initVMConfiguration(&aJvm, m_xContext->getServiceManager(), m_xContext); +// return aJvm.isEnabled(); + //ToDo + bool bEnabled = false; + if (jfw_getEnabled( & bEnabled) != JFW_E_NONE) + throw css::uno::RuntimeException(); + return bEnabled; +} + +sal_Bool SAL_CALL JavaVirtualMachine::isThreadAttached() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), getXWeak()); + // TODO isThreadAttached only returns true if the thread was attached via + // registerThread: + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + return pStack != nullptr && !pStack->empty(); +} + +void SAL_CALL JavaVirtualMachine::registerThread() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + if (!m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::registerThread: null VirtualMachine", + getXWeak()); + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + if (pStack == nullptr) + { + pStack = new GuardStack; + m_aAttachGuards.setData(pStack); + } + try + { + pStack->push( + new jvmaccess::VirtualMachine::AttachGuard( + m_xUnoVirtualMachine->getVirtualMachine())); + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "JavaVirtualMachine::registerThread: jvmaccess::" + "VirtualMachine::AttachGuard::CreationException", + getXWeak(), anyEx ); + } +} + +void SAL_CALL JavaVirtualMachine::revokeThread() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + if (!m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::revokeThread: null VirtualMachine", + getXWeak()); + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + if (pStack == nullptr || pStack->empty()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::revokeThread: no matching registerThread", + getXWeak()); + delete pStack->top(); + pStack->pop(); +} + +void SAL_CALL +JavaVirtualMachine::disposing(css::lang::EventObject const & rSource) +{ + osl::MutexGuard aGuard(m_aMutex); + if (rSource.Source == m_xInetConfiguration) + m_xInetConfiguration.clear(); + if (rSource.Source == m_xJavaConfiguration) + m_xJavaConfiguration.clear(); +} + +void SAL_CALL JavaVirtualMachine::elementInserted( + css::container::ContainerEvent const &) +{} + +void SAL_CALL JavaVirtualMachine::elementRemoved( + css::container::ContainerEvent const &) +{} + +// If a user changes the setting, for example for proxy settings, then this +// function will be called from the configuration manager. Even if the .xml +// file does not contain an entry yet and that entry has to be inserted, this +// function will be called. We call java.lang.System.setProperty for the new +// values. +void SAL_CALL JavaVirtualMachine::elementReplaced( + css::container::ContainerEvent const & rEvent) +{ + // TODO Using the new value stored in rEvent is wrong here. If two threads + // receive different elementReplaced calls in quick succession, it is + // unspecified which changes the JVM's system properties last. A correct + // solution must atomically (i.e., protected by a mutex) read the latest + // value from the configuration and set it as a system property at the JVM. + + OUString aAccessor; + rEvent.Accessor >>= aAccessor; + OUString aPropertyName; + OUString aPropertyValue; + bool bSecurityChanged = false; + if ( aAccessor == "ooInetProxyType" ) + { + // Proxy none, manually + sal_Int32 value = 0; + rEvent.Element >>= value; + setINetSettingsInVM(value != 0); + return; + } + else if ( aAccessor == "ooInetHTTPProxyName" ) + { + aPropertyName = "http.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetHTTPProxyPort" ) + { + aPropertyName = "http.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetHTTPSProxyName" ) + { + aPropertyName = "https.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetHTTPSProxyPort" ) + { + aPropertyName = "https.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetNoProxy" ) + { + aPropertyName = "http.nonProxyHosts"; + rEvent.Element >>= aPropertyValue; + aPropertyValue = aPropertyValue.replace(';', '|'); + } + else if ( aAccessor == "NetAccess" ) + { + aPropertyName = "appletviewer.security.mode"; + sal_Int32 n = 0; + if (rEvent.Element >>= n) + switch (n) + { + case 0: + aPropertyValue = "host"; + break; + case 1: + aPropertyValue = "unrestricted"; + break; + case 3: + aPropertyValue = "none"; + break; + } + else + return; + bSecurityChanged = true; + } + else if ( aAccessor == "Security" ) + { + aPropertyName = "stardiv.security.disableSecurity"; + bool b; + if (rEvent.Element >>= b) + if (b) + aPropertyValue = "false"; + else + aPropertyValue = "true"; + else + return; + bSecurityChanged = true; + } + else + return; + + rtl::Reference< jvmaccess::VirtualMachine > xVirtualMachine; + { + osl::MutexGuard aGuard(m_aMutex); + if (m_xUnoVirtualMachine.is()) { + xVirtualMachine = m_xUnoVirtualMachine->getVirtualMachine(); + } + } + if (!xVirtualMachine.is()) + return; + + try + { + jvmaccess::VirtualMachine::AttachGuard aAttachGuard( + xVirtualMachine); + JNIEnv * pJNIEnv = aAttachGuard.getEnvironment(); + + // call java.lang.System.setProperty + // String setProperty( String key, String value) + jclass jcSystem= pJNIEnv->FindClass("java/lang/System"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/lang/System", nullptr); + jmethodID jmSetProps= pJNIEnv->GetStaticMethodID( jcSystem, "setProperty","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.setProperty", nullptr); + + jstring jsPropName= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyName.getStr()), aPropertyName.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + + // remove the property if it does not have a value ( user left the dialog field empty) + // or if the port is set to 0 + aPropertyValue= aPropertyValue.trim(); + if( aPropertyValue.isEmpty() || + ((aPropertyName == "http.proxyPort" /*|| aPropertyName == "socksProxyPort"*/) && aPropertyValue == "0") + ) + { + // call java.lang.System.getProperties + jmethodID jmGetProps= pJNIEnv->GetStaticMethodID( jcSystem, "getProperties","()Ljava/util/Properties;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getProperties", nullptr); + jobject joProperties= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetProps); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.getProperties", nullptr); + // call java.util.Properties.remove + jclass jcProperties= pJNIEnv->FindClass("java/util/Properties"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/util/Properties", nullptr); + jmethodID jmRemove= pJNIEnv->GetMethodID( jcProperties, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.util.Properties.remove", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsPropName); + } + else + { + // Change the Value of the property + jstring jsPropValue= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyValue.getStr()), aPropertyValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsPropName, jsPropValue); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + + // If the settings for Security and NetAccess changed then we have to notify the SandboxSecurity + // SecurityManager + // call System.getSecurityManager() + if (bSecurityChanged) + { + jmethodID jmGetSecur= pJNIEnv->GetStaticMethodID( jcSystem,"getSecurityManager","()Ljava/lang/SecurityManager;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getSecurityManager", nullptr); + jobject joSecur= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetSecur); + if (joSecur != nullptr) + { + // Make sure the SecurityManager is our SandboxSecurity + // FindClass("com.sun.star.lib.sandbox.SandboxSecurityManager" only worked at the first time + // this code was executed. Maybe it is a security feature. However, all attempts to debug the + // SandboxSecurity class (maybe the VM invokes checkPackageAccess) failed. +// jclass jcSandboxSec= pJNIEnv->FindClass("com.sun.star.lib.sandbox.SandboxSecurity"); +// if(pJNIEnv->ExceptionOccurred()) throw RuntimeException("JNI:FindClass com.sun.star.lib.sandbox.SandboxSecurity"); +// jboolean bIsSand= pJNIEnv->IsInstanceOf( joSecur, jcSandboxSec); + // The SecurityManagers class Name must be com.sun.star.lib.sandbox.SandboxSecurity + jclass jcSec= pJNIEnv->GetObjectClass( joSecur); + jclass jcClass= pJNIEnv->FindClass("java/lang/Class"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java.lang.Class", nullptr); + jmethodID jmName= pJNIEnv->GetMethodID( jcClass,"getName","()Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.lang.Class.getName", nullptr); + jstring jsClass= static_cast<jstring>(pJNIEnv->CallObjectMethod( jcSec, jmName)); + const jchar* jcharName= pJNIEnv->GetStringChars( jsClass, nullptr); + OUString sName(reinterpret_cast<sal_Unicode const *>(jcharName)); + bool bIsSandbox; + bIsSandbox = sName == "com.sun.star.lib.sandbox.SandboxSecurity"; + pJNIEnv->ReleaseStringChars( jsClass, jcharName); + + if (bIsSandbox) + { + // call SandboxSecurity.reset + jmethodID jmReset= pJNIEnv->GetMethodID( jcSec,"reset","()V"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID com.sun.star.lib.sandbox.SandboxSecurity.reset", nullptr); + pJNIEnv->CallVoidMethod( joSecur, jmReset); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallVoidMethod com.sun.star.lib.sandbox.SandboxSecurity.reset", nullptr); + } + } + } + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException", + getXWeak(), anyEx ); + } +} + +JavaVirtualMachine::~JavaVirtualMachine() +{ + if (m_xInetConfiguration.is()) + // We should never get here, but just in case... + try + { + m_xInetConfiguration->removeContainerListener(this); + } + catch (css::uno::Exception &) + { + OSL_FAIL("com.sun.star.uno.Exception caught"); + } + if (m_xJavaConfiguration.is()) + // We should never get here, but just in case... + try + { + m_xJavaConfiguration->removeContainerListener(this); + } + catch (css::uno::Exception &) + { + OSL_FAIL("com.sun.star.uno.Exception caught"); + } +} + +void SAL_CALL JavaVirtualMachine::disposing() +{ + css::uno::Reference< css::container::XContainer > xContainer1; + css::uno::Reference< css::container::XContainer > xContainer2; + { + osl::MutexGuard aGuard(m_aMutex); + m_bDisposed = true; + xContainer1 = m_xInetConfiguration; + m_xInetConfiguration.clear(); + xContainer2 = m_xJavaConfiguration; + m_xJavaConfiguration.clear(); + } + if (xContainer1.is()) + xContainer1->removeContainerListener(this); + if (xContainer2.is()) + xContainer2->removeContainerListener(this); +} + +/*We listen to changes in the configuration. For example, the user changes the proxy + settings in the options dialog (menu tools). Then we are notified of this change and + if the java vm is already running we change the properties (System.lang.System.setProperties) + through JNI. + To receive notifications this class implements XContainerListener. +*/ +void JavaVirtualMachine::registerConfigChangesListener() +{ + try + { + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider( + m_xContext->getValueByName( + "/singletons/com.sun.star.configuration.theDefaultProvider"), + css::uno::UNO_QUERY); + + if (xConfigProvider.is()) + { + // We register this instance as listener to changes in org.openoffice.Inet/Settings + // arguments for ConfigurationAccess + css::uno::Sequence<css::uno::Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", css::uno::Any(OUString("org.openoffice.Inet/Settings"))}, + {"depth", css::uno::Any(sal_Int32(-1))} + })); + m_xInetConfiguration.set( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments), + css::uno::UNO_QUERY); + + if (m_xInetConfiguration.is()) + m_xInetConfiguration->addContainerListener(this); + + // now register as listener to changes in org.openoffice.Java/VirtualMachine + css::uno::Sequence<css::uno::Any> aArguments2(comphelper::InitAnyPropertySequence( + { + {"nodepath", css::uno::Any(OUString("org.openoffice.Office.Java/VirtualMachine"))}, + {"depth", css::uno::Any(sal_Int32(-1))} // depth: -1 means unlimited + })); + m_xJavaConfiguration.set( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments2), + css::uno::UNO_QUERY); + + if (m_xJavaConfiguration.is()) + m_xJavaConfiguration->addContainerListener(this); + } + }catch(const css::uno::Exception & e) + { + SAL_INFO("stoc", "could not set up listener for Configuration because of >" << e << "<"); + } +} + +// param true: all Inet setting are set as Java Properties on a live VM. +// false: the Java net properties are set to empty value. +void JavaVirtualMachine::setINetSettingsInVM(bool set_reset) +{ + osl::MutexGuard aGuard(m_aMutex); + try + { + if (m_xUnoVirtualMachine.is()) + { + jvmaccess::VirtualMachine::AttachGuard aAttachGuard( + m_xUnoVirtualMachine->getVirtualMachine()); + JNIEnv * pJNIEnv = aAttachGuard.getEnvironment(); + + // The Java Properties + OUString sHttpProxyHost("http.proxyHost"); + OUString sHttpProxyPort("http.proxyPort"); + OUString sHttpNonProxyHosts("http.nonProxyHosts"); + + // create Java Properties as JNI strings + jstring jsHttpProxyHost= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpProxyHost.getStr()), sHttpProxyHost.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpProxyPort= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpProxyPort.getStr()), sHttpProxyPort.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpNonProxyHosts= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpNonProxyHosts.getStr()), sHttpNonProxyHosts.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + + // prepare java.lang.System.setProperty + jclass jcSystem= pJNIEnv->FindClass("java/lang/System"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/lang/System", nullptr); + jmethodID jmSetProps= pJNIEnv->GetStaticMethodID( jcSystem, "setProperty","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.setProperty", nullptr); + + // call java.lang.System.getProperties + jmethodID jmGetProps= pJNIEnv->GetStaticMethodID( jcSystem, "getProperties","()Ljava/util/Properties;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getProperties", nullptr); + jobject joProperties= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetProps); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.getProperties", nullptr); + // prepare java.util.Properties.remove + jclass jcProperties= pJNIEnv->FindClass("java/util/Properties"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/util/Properties", nullptr); + + if (set_reset) + { + // Set all network properties with the VM + JVM jvm; + getINetPropsFromConfig( &jvm, m_xContext->getServiceManager(), m_xContext); + const ::std::vector< OUString> & Props = jvm.getProperties(); + + for( auto& prop : Props) + { + sal_Int32 index= prop.indexOf( '='); + std::u16string_view propName= prop.subView( 0, index); + OUString propValue= prop.copy( index + 1); + + if (propName == sHttpProxyHost) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpProxyHost, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpProxyPort) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpProxyPort, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpNonProxyHosts) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpNonProxyHosts, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + } + } + else + { + // call java.util.Properties.remove + jmethodID jmRemove= pJNIEnv->GetMethodID( jcProperties, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.util.Property.remove", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpProxyHost); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpProxyPort); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpNonProxyHosts); + } + } + } + catch (css::uno::RuntimeException &) + { + OSL_FAIL("RuntimeException"); + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + OSL_FAIL("jvmaccess::VirtualMachine::AttachGuard::CreationException"); + } +} + +void JavaVirtualMachine::setUpUnoVirtualMachine(JNIEnv * environment) { + css::uno::Reference< css::util::XMacroExpander > exp = css::util::theMacroExpander::get(m_xContext); + OUString baseUrl; + try { + baseUrl = exp->expandMacros("$URE_INTERNAL_JAVA_DIR/"); + } catch (css::lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "css::lang::IllegalArgumentException", + getXWeak(), anyEx ); + } + OUString classPath; + try { + classPath = exp->expandMacros("$URE_INTERNAL_JAVA_CLASSPATH"); + } catch (css::lang::IllegalArgumentException &) {} + jclass class_URLClassLoader = environment->FindClass( + "java/net/URLClassLoader"); + if (class_URLClassLoader == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URLClassLoader = environment->GetMethodID( + class_URLClassLoader, "<init>", "([Ljava/net/URL;)V"); + if (ctor_URLClassLoader == nullptr) { + handleJniException(environment); + } + jclass class_URL = environment->FindClass("java/net/URL"); + if (class_URL == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URL_1 = environment->GetMethodID( + class_URL, "<init>", "(Ljava/lang/String;)V"); + if (ctor_URL_1 == nullptr) { + handleJniException(environment); + } + jvalue args[3]; + args[0].l = environment->NewString( + reinterpret_cast< jchar const * >(baseUrl.getStr()), + static_cast< jsize >(baseUrl.getLength())); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jobject base = environment->NewObjectA(class_URL, ctor_URL_1, args); + if (base == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URL_2 = environment->GetMethodID( + class_URL, "<init>", "(Ljava/net/URL;Ljava/lang/String;)V"); + if (ctor_URL_2 == nullptr) { + handleJniException(environment); + } + jobjectArray classpath = jvmaccess::ClassPath::translateToUrls( + m_xContext, environment, classPath); + if (classpath == nullptr) { + handleJniException(environment); + } + args[0].l = base; + args[1].l = environment->NewStringUTF("unoloader.jar"); + if (args[1].l == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewObjectA(class_URL, ctor_URL_2, args); + if (args[0].l == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewObjectArray(1, class_URL, args[0].l); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jobject cl1 = environment->NewObjectA( + class_URLClassLoader, ctor_URLClassLoader, args); + if (cl1 == nullptr) { + handleJniException(environment); + } + jmethodID method_loadClass = environment->GetMethodID( + class_URLClassLoader, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + if (method_loadClass == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewStringUTF( + "com.sun.star.lib.unoloader.UnoClassLoader"); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jclass class_UnoClassLoader = static_cast< jclass >( + environment->CallObjectMethodA(cl1, method_loadClass, args)); + if (class_UnoClassLoader == nullptr) { + handleJniException(environment); + } + jmethodID ctor_UnoClassLoader = environment->GetMethodID( + class_UnoClassLoader, "<init>", + "(Ljava/net/URL;[Ljava/net/URL;Ljava/lang/ClassLoader;)V"); + if (ctor_UnoClassLoader == nullptr) { + handleJniException(environment); + } + args[0].l = base; + args[1].l = classpath; + args[2].l = cl1; + jobject cl2 = environment->NewObjectA( + class_UnoClassLoader, ctor_UnoClassLoader, args); + if (cl2 == nullptr) { + handleJniException(environment); + } + try { + m_xUnoVirtualMachine = new jvmaccess::UnoVirtualMachine( + m_xVirtualMachine, cl2); + } catch (jvmaccess::UnoVirtualMachine::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::UnoVirtualMachine::CreationException", + getXWeak(), anyEx ); + } +} + +void JavaVirtualMachine::handleJniException(JNIEnv * environment) { +#if defined DBG_UTIL + environment->ExceptionDescribe(); +#else + environment->ExceptionClear(); +#endif + throw css::uno::RuntimeException( + "JNI exception occurred", + getXWeak()); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_JavaVM_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new JavaVirtualMachine(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/javavm.hxx b/stoc/source/javavm/javavm.hxx new file mode 100644 index 0000000000..b4f79d1f9f --- /dev/null +++ b/stoc/source/javavm/javavm.hxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/java/XJavaThreadRegister_11.hpp> +#include <com/sun/star/java/XJavaVM.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <osl/thread.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace container { class XContainer; } + namespace uno { class XComponentContext; } +} +namespace jvmaccess { + class UnoVirtualMachine; + class VirtualMachine; +} + +namespace stoc_javavm { + +class JavaVirtualMachine: + private cppu::BaseMutex, + public cppu::WeakComponentImplHelper< + css::lang::XInitialization, css::lang::XServiceInfo, css::java::XJavaVM, + css::java::XJavaThreadRegister_11, css::container::XContainerListener> +{ +public: + explicit JavaVirtualMachine( + css::uno::Reference< + css::uno::XComponentContext > xContext); + + // XInitialization + virtual void SAL_CALL + initialize(css::uno::Sequence< css::uno::Any > const & + rArguments) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL + supportsService(OUString const & rServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + // XJavaVM + virtual css::uno::Any SAL_CALL + getJavaVM(css::uno::Sequence< sal_Int8 > const & rProcessId) override; + + virtual sal_Bool SAL_CALL isVMStarted() override; + + virtual sal_Bool SAL_CALL isVMEnabled() override; + + // XJavaThreadRegister_11 + virtual sal_Bool SAL_CALL isThreadAttached() override; + + virtual void SAL_CALL registerThread() override; + + virtual void SAL_CALL revokeThread() override; + + // XContainerListener + virtual void SAL_CALL + disposing(css::lang::EventObject const & rSource) override; + + virtual void SAL_CALL + elementInserted(css::container::ContainerEvent const & rEvent) override; + + virtual void SAL_CALL + elementRemoved(css::container::ContainerEvent const & rEvent) override; + + virtual void SAL_CALL + elementReplaced(css::container::ContainerEvent const & rEvent) override; + +private: + JavaVirtualMachine(JavaVirtualMachine const &) = delete; + void operator =(const JavaVirtualMachine&) = delete; + + virtual ~JavaVirtualMachine() override; + + virtual void SAL_CALL disposing() override; + + void registerConfigChangesListener(); + + void setINetSettingsInVM(bool set_reset); + + void setUpUnoVirtualMachine(JNIEnv * environment); + + void handleJniException(JNIEnv * environment); + + css::uno::Reference< css::uno::XComponentContext > + m_xContext; + + // the following are controlled by BaseMutex::m_aMutex: + bool m_bDisposed; + rtl::Reference< jvmaccess::VirtualMachine > m_xVirtualMachine; + rtl::Reference< jvmaccess::UnoVirtualMachine > m_xUnoVirtualMachine; + JavaVM * m_pJavaVm; + // If the first creation of Java failed and this flag is set then the + // next call to getJavaVM throws a RuntimException. This is useful when + // the second attempt to create Java might cause a crash. + css::uno::Reference< css::container::XContainer > + m_xInetConfiguration; + css::uno::Reference< css::container::XContainer > + m_xJavaConfiguration; // for Java settings + + osl::ThreadData m_aAttachGuards; +}; + +} + +#endif // INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/jvmargs.cxx b/stoc/source/javavm/jvmargs.cxx new file mode 100644 index 0000000000..f98f22ec0c --- /dev/null +++ b/stoc/source/javavm/jvmargs.cxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "jvmargs.hxx" +#include <rtl/ustring.hxx> + +namespace stoc_javavm +{ +JVM::JVM() noexcept //: _enabled(sal_False) +{ +} + +void JVM::pushProp(const OUString& property) { _props.push_back(property); } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/jvmargs.hxx b/stoc/source/javavm/jvmargs.hxx new file mode 100644 index 0000000000..adc41ffa15 --- /dev/null +++ b/stoc/source/javavm/jvmargs.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_JVMARGS_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_JVMARGS_HXX + + +#include <vector> +#include <rtl/ustring.hxx> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +extern "C" { + typedef jint JNICALL JNI_InitArgs_Type(void *); + typedef jint JNICALL JNI_CreateVM_Type(JavaVM **, JNIEnv **, void *); + +} + +namespace stoc_javavm { + + class JVM { + ::std::vector<OUString> _props; + + public: + JVM() noexcept; + + void pushProp(const OUString & uString); + const ::std::vector< OUString> & getProperties() const { return _props;} + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/loader/dllcomponentloader.cxx b/stoc/source/loader/dllcomponentloader.cxx new file mode 100644 index 0000000000..43f20ad0a5 --- /dev/null +++ b/stoc/source/loader/dllcomponentloader.cxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <osl/diagnose.h> +#include <rtl/ustring.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/shlib.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/bootstrap.hxx> + +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace com::sun::star::registry { class XRegistryKey; } + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::loader; +using namespace css::lang; +using namespace css::registry; +using namespace cppu; +using namespace osl; + +namespace { + +class DllComponentLoader + : public WeakImplHelper< XImplementationLoader, + XInitialization, + XServiceInfo > +{ +public: + explicit DllComponentLoader( const Reference<XComponentContext> & xCtx ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XImplementationLoader + virtual Reference<XInterface> SAL_CALL activate( const OUString& implementationName, const OUString& implementationLoaderUrl, const OUString& locationUrl, const Reference<XRegistryKey>& xKey ) override; + virtual sal_Bool SAL_CALL writeRegistryInfo( const Reference<XRegistryKey>& xKey, const OUString& implementationLoaderUrl, const OUString& locationUrl ) override; + +private: + Reference<XMultiServiceFactory> m_xSMgr; +}; + + +DllComponentLoader::DllComponentLoader( const Reference<XComponentContext> & xCtx ) +{ + m_xSMgr.set( xCtx->getServiceManager(), UNO_QUERY ); +} + +OUString SAL_CALL DllComponentLoader::getImplementationName( ) +{ + return "com.sun.star.comp.stoc.DLLComponentLoader"; +} + +sal_Bool SAL_CALL DllComponentLoader::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL DllComponentLoader::getSupportedServiceNames( ) +{ + return { "com.sun.star.loader.SharedLibrary" }; +} + + +void DllComponentLoader::initialize( const css::uno::Sequence< css::uno::Any >& ) +{ + OSL_FAIL( "dllcomponentloader::initialize should not be called !" ); +// if( aArgs.getLength() != 1 ) +// { +// throw IllegalArgumentException(); +// } + +// Reference< XMultiServiceFactory > rServiceManager; + +// if( aArgs.getConstArray()[0].getValueType().getTypeClass() == TypeClass_INTERFACE ) +// { +// aArgs.getConstArray()[0] >>= rServiceManager; +// } + +// if( !rServiceManager.is() ) +// { +// throw IllegalArgumentException(); +// } + +// m_xSMgr = rServiceManager; +} + + +Reference<XInterface> SAL_CALL DllComponentLoader::activate( + const OUString & rImplName, const OUString &, const OUString & rLibName, + const Reference< XRegistryKey > & ) +{ + return loadSharedLibComponentFactory( + cppu::bootstrap_expandUri(rLibName), OUString(), rImplName, m_xSMgr, + css::uno::Reference<css::registry::XRegistryKey>()); +} + + +sal_Bool SAL_CALL DllComponentLoader::writeRegistryInfo( + const Reference< XRegistryKey > & xKey, const OUString &, const OUString & rLibName ) +{ +#ifdef DISABLE_DYNLOADING + (void) xKey; + (void) rLibName; + OSL_FAIL( "DllComponentLoader::writeRegistryInfo() should not be called I think?" ); + return sal_False; +#else + writeSharedLibComponentInfo( + cppu::bootstrap_expandUri(rLibName), OUString(), m_xSMgr, xKey ); + return true; +#endif +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_DLLComponentLoader_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DllComponentLoader(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/namingservice/namingservice.component b/stoc/source/namingservice/namingservice.component new file mode 100644 index 0000000000..3ba192e866 --- /dev/null +++ b/stoc/source/namingservice/namingservice.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.NamingService" + constructor="stoc_NamingService_Impl_get_implementation"> + <service name="com.sun.star.uno.NamingService"/> + </implementation> +</component> diff --git a/stoc/source/namingservice/namingservice.cxx b/stoc/source/namingservice/namingservice.cxx new file mode 100644 index 0000000000..acabb7370c --- /dev/null +++ b/stoc/source/namingservice/namingservice.cxx @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/uno/XNamingService.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <mutex> +#include <unordered_map> + +using namespace cppu; +using namespace osl; + +using namespace css::uno; +using namespace css::lang; +using namespace css::registry; + + +namespace stoc_namingservice +{ + +typedef std::unordered_map< OUString, Reference<XInterface > > HashMap_OWString_Interface; + +namespace { + +class NamingService_Impl + : public WeakImplHelper < XServiceInfo, XNamingService > +{ + std::mutex aMutex; + HashMap_OWString_Interface aMap; +public: + NamingService_Impl(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRegisteredObject( const OUString& Name ) override; + virtual void SAL_CALL registerObject( const OUString& Name, const css::uno::Reference< css::uno::XInterface >& Object ) override; + virtual void SAL_CALL revokeObject( const OUString& Name ) override; +}; + +} + + + +NamingService_Impl::NamingService_Impl() {} + +// XServiceInfo +OUString NamingService_Impl::getImplementationName() +{ + return "com.sun.star.comp.stoc.NamingService"; +} + +// XServiceInfo +sal_Bool NamingService_Impl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +// XServiceInfo +Sequence< OUString > NamingService_Impl::getSupportedServiceNames() +{ + return { "com.sun.star.uno.NamingService" }; +} + +// XServiceInfo +Reference< XInterface > NamingService_Impl::getRegisteredObject( const OUString& Name ) +{ + std::scoped_lock aGuard( aMutex ); + Reference< XInterface > xRet; + HashMap_OWString_Interface::iterator aIt = aMap.find( Name ); + if( aIt != aMap.end() ) + xRet = (*aIt).second; + return xRet; +} + +// XServiceInfo +void NamingService_Impl::registerObject( const OUString& Name, const Reference< XInterface >& Object ) +{ + std::scoped_lock aGuard( aMutex ); + aMap[ Name ] = Object; +} + +// XServiceInfo +void NamingService_Impl::revokeObject( const OUString& Name ) +{ + std::scoped_lock aGuard( aMutex ); + aMap.erase( Name ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_NamingService_Impl_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new stoc_namingservice::NamingService_Impl()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/proxy_factory/proxyfac.component b/stoc/source/proxy_factory/proxyfac.component new file mode 100644 index 0000000000..b069f88dff --- /dev/null +++ b/stoc/source/proxy_factory/proxyfac.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.reflection.ProxyFactory" + constructor="stoc_FactoryImpl_get_implementation" single-instance="true"> + <service name="com.sun.star.reflection.ProxyFactory"/> + </implementation> +</component> diff --git a/stoc/source/proxy_factory/proxyfac.cxx b/stoc/source/proxy_factory/proxyfac.cxx new file mode 100644 index 0000000000..6745d0163c --- /dev/null +++ b/stoc/source/proxy_factory/proxyfac.cxx @@ -0,0 +1,411 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <osl/diagnose.h> +#include <osl/interlck.h> +#include <rtl/ref.hxx> +#include <uno/dispatcher.hxx> +#include <uno/data.h> +#include <uno/lbnames.h> +#include <uno/mapping.hxx> +#include <uno/environment.hxx> +#include <typelib/typedescription.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakagg.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/reflection/XProxyFactory.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <utility> + + +using namespace ::com::sun::star; +using namespace css::uno; + + +namespace +{ + +struct FactoryImpl : public ::cppu::WeakImplHelper< lang::XServiceInfo, + reflection::XProxyFactory > +{ + Environment m_uno_env; + Environment m_cpp_env; + Mapping m_uno2cpp; + Mapping m_cpp2uno; + + UnoInterfaceReference binuno_queryInterface( + UnoInterfaceReference const & unoI, + typelib_InterfaceTypeDescription * pTypeDescr ); + + FactoryImpl(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XProxyFactory + virtual Reference< XAggregation > SAL_CALL createProxy( + Reference< XInterface > const & xTarget ) override; +}; + + +UnoInterfaceReference FactoryImpl::binuno_queryInterface( + UnoInterfaceReference const & unoI, + typelib_InterfaceTypeDescription * pTypeDescr ) +{ + // init queryInterface() td + static typelib_TypeDescription* s_pQITD = []() { + typelib_TypeDescription* pTXInterfaceDescr = nullptr; + TYPELIB_DANGER_GET(&pTXInterfaceDescr, cppu::UnoType<XInterface>::get().getTypeLibType()); + typelib_TypeDescription* pQITD = nullptr; + typelib_typedescriptionreference_getDescription( + &pQITD, reinterpret_cast<typelib_InterfaceTypeDescription*>(pTXInterfaceDescr) + ->ppAllMembers[0]); + TYPELIB_DANGER_RELEASE(pTXInterfaceDescr); + return pQITD; + }(); + + void * args[ 1 ]; + args[ 0 ] = &reinterpret_cast< typelib_TypeDescription * >( + pTypeDescr )->pWeakRef; + uno_Any ret_val, exc_space; + uno_Any * exc = &exc_space; + + unoI.dispatch( s_pQITD, &ret_val, args, &exc ); + + if (exc == nullptr) + { + UnoInterfaceReference ret; + if (ret_val.pType->eTypeClass == typelib_TypeClass_INTERFACE) + { + ret.set( *static_cast< uno_Interface ** >(ret_val.pData), + SAL_NO_ACQUIRE ); + typelib_typedescriptionreference_release( ret_val.pType ); + } + else + { + uno_any_destruct( &ret_val, nullptr ); + } + return ret; + } + else + { + // exception occurred: + OSL_ENSURE( + typelib_typedescriptionreference_isAssignableFrom( cppu::UnoType<RuntimeException>::get().getTypeLibType(), + exc->pType ), + "### RuntimeException expected!" ); + Any cpp_exc; + uno_type_copyAndConvertData( + &cpp_exc, exc, cppu::UnoType<decltype(cpp_exc)>::get().getTypeLibType(), + m_uno2cpp.get() ); + uno_any_destruct( exc, nullptr ); + ::cppu::throwException( cpp_exc ); + OSL_ASSERT( false ); // way of no return + return UnoInterfaceReference(); // for dummy + } +} + + +struct ProxyRoot : public ::cppu::OWeakAggObject +{ + // XAggregation + virtual Any SAL_CALL queryAggregation( Type const & rType ) override; + + ProxyRoot( ::rtl::Reference< FactoryImpl > factory, + Reference< XInterface > const & xTarget ); + + ::rtl::Reference< FactoryImpl > m_factory; + +private: + UnoInterfaceReference m_target; +}; + + +struct binuno_Proxy : public uno_Interface +{ + oslInterlockedCount m_nRefCount; + ::rtl::Reference< ProxyRoot > m_root; + UnoInterfaceReference m_target; + OUString m_oid; + TypeDescription m_typeDescr; + + binuno_Proxy( + ::rtl::Reference< ProxyRoot > root, + UnoInterfaceReference target, + OUString oid, TypeDescription typeDescr ); +}; + +extern "C" +{ + + +static void binuno_proxy_free( + uno_ExtEnvironment * pEnv, void * pProxy ) +{ + binuno_Proxy * proxy = static_cast< binuno_Proxy * >( + static_cast< uno_Interface * >( pProxy ) ); + OSL_ASSERT( proxy->m_root->m_factory->m_uno_env.get()->pExtEnv == pEnv ); + delete proxy; +} + + +static void binuno_proxy_acquire( uno_Interface * pUnoI ) +{ + binuno_Proxy * that = static_cast< binuno_Proxy * >( pUnoI ); + if (osl_atomic_increment( &that->m_nRefCount ) != 1) + return; + + // rebirth of zombie + uno_ExtEnvironment * uno_env = + that->m_root->m_factory->m_uno_env.get()->pExtEnv; + OSL_ASSERT( uno_env != nullptr ); + (*uno_env->registerProxyInterface)( + uno_env, reinterpret_cast< void ** >( &pUnoI ), binuno_proxy_free, + that->m_oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + that->m_typeDescr.get() ) ); + OSL_ASSERT( that == static_cast< binuno_Proxy * >( pUnoI ) ); +} + + +static void binuno_proxy_release( uno_Interface * pUnoI ) +{ + binuno_Proxy * that = static_cast< binuno_Proxy * >( pUnoI ); + if (osl_atomic_decrement( &that->m_nRefCount ) == 0) + { + uno_ExtEnvironment * uno_env = + that->m_root->m_factory->m_uno_env.get()->pExtEnv; + OSL_ASSERT( uno_env != nullptr ); + (*uno_env->revokeInterface)( uno_env, pUnoI ); + } +} + + +static void binuno_proxy_dispatch( + uno_Interface * pUnoI, const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs [], uno_Any ** ppException ) +{ + binuno_Proxy * that = static_cast< binuno_Proxy * >( pUnoI ); + switch (reinterpret_cast< typelib_InterfaceMemberTypeDescription const * >( + pMemberType )->nPosition) + { + case 0: // queryInterface() + { + try + { + Type const & rType = + *static_cast< Type const * >( pArgs[ 0 ] ); + Any ret( that->m_root->queryInterface( rType ) ); + uno_type_copyAndConvertData( + pReturn, &ret, cppu::UnoType<decltype(ret)>::get().getTypeLibType(), + that->m_root->m_factory->m_cpp2uno.get() ); + *ppException = nullptr; // no exc + } + catch (RuntimeException &) + { + Any exc( ::cppu::getCaughtException() ); + uno_type_any_constructAndConvert( + *ppException, const_cast< void * >(exc.getValue()), + exc.getValueTypeRef(), + that->m_root->m_factory->m_cpp2uno.get() ); + } + break; + } + case 1: // acquire() + binuno_proxy_acquire( pUnoI ); + *ppException = nullptr; // no exc + break; + case 2: // release() + binuno_proxy_release( pUnoI ); + *ppException = nullptr; // no exc + break; + default: + that->m_target.dispatch( pMemberType, pReturn, pArgs, ppException ); + break; + } +} + +} + + +binuno_Proxy::binuno_Proxy( + ::rtl::Reference< ProxyRoot > root, + UnoInterfaceReference target, + OUString oid, TypeDescription typeDescr ) + : m_nRefCount( 1 ), + m_root(std::move( root )), + m_target(std::move( target )), + m_oid(std::move( oid )), + m_typeDescr(std::move( typeDescr )) +{ + uno_Interface::acquire = binuno_proxy_acquire; + uno_Interface::release = binuno_proxy_release; + uno_Interface::pDispatcher = binuno_proxy_dispatch; +} + +ProxyRoot::ProxyRoot( + ::rtl::Reference< FactoryImpl > factory, + Reference< XInterface > const & xTarget ) + : m_factory(std::move( factory )) +{ + m_factory->m_cpp2uno.mapInterface( + reinterpret_cast< void ** >( &m_target.m_pUnoI ), xTarget.get(), + cppu::UnoType<decltype(xTarget)>::get() ); + OSL_ENSURE( m_target.is(), "### mapping interface failed!" ); +} + + +Any ProxyRoot::queryAggregation( Type const & rType ) +{ + Any ret( OWeakAggObject::queryAggregation( rType ) ); + if (! ret.hasValue()) + { + typelib_TypeDescription * pTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pTypeDescr, rType.getTypeLibType() ); + try + { + Reference< XInterface > xProxy; + uno_ExtEnvironment * cpp_env = m_factory->m_cpp_env.get()->pExtEnv; + OSL_ASSERT( cpp_env != nullptr ); + + // mind a new delegator, calculate current root: + Reference< XInterface > xRoot( + static_cast< OWeakObject * >(this), UNO_QUERY_THROW ); + OUString oid; + (*cpp_env->getObjectIdentifier)( cpp_env, &oid.pData, xRoot.get() ); + OSL_ASSERT( !oid.isEmpty() ); + + (*cpp_env->getRegisteredInterface)( + cpp_env, reinterpret_cast< void ** >( &xProxy ), + oid.pData, reinterpret_cast< + typelib_InterfaceTypeDescription * >(pTypeDescr) ); + if (! xProxy.is()) + { + // perform query on target: + UnoInterfaceReference proxy_target( + m_factory->binuno_queryInterface( + m_target, reinterpret_cast< + typelib_InterfaceTypeDescription * >(pTypeDescr) ) ); + if (proxy_target.is()) + { + // ensure root's object entries: + UnoInterfaceReference root; + m_factory->m_cpp2uno.mapInterface( + reinterpret_cast< void ** >( &root.m_pUnoI ), + xRoot.get(), cppu::UnoType<decltype(xRoot)>::get() ); + + UnoInterfaceReference proxy( + // ref count initially 1: + new binuno_Proxy( this, proxy_target, oid, pTypeDescr ), + SAL_NO_ACQUIRE ); + uno_ExtEnvironment * uno_env = + m_factory->m_uno_env.get()->pExtEnv; + OSL_ASSERT( uno_env != nullptr ); + (*uno_env->registerProxyInterface)( + uno_env, reinterpret_cast< void ** >( &proxy.m_pUnoI ), + binuno_proxy_free, oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + pTypeDescr ) ); + + m_factory->m_uno2cpp.mapInterface( + reinterpret_cast< void ** >( &xProxy ), + proxy.get(), pTypeDescr ); + } + } + if (xProxy.is()) + ret.setValue( &xProxy, pTypeDescr ); + } + catch (...) // finally + { + TYPELIB_DANGER_RELEASE( pTypeDescr ); + throw; + } + TYPELIB_DANGER_RELEASE( pTypeDescr ); + } + return ret; +} + + +FactoryImpl::FactoryImpl() +{ + OUString uno = UNO_LB_UNO; + OUString cpp = CPPU_CURRENT_LANGUAGE_BINDING_NAME; + + uno_getEnvironment( + reinterpret_cast< uno_Environment ** >( &m_uno_env ), uno.pData, nullptr ); + OSL_ENSURE( m_uno_env.is(), "### cannot get binary uno env!" ); + + uno_getEnvironment( + reinterpret_cast< uno_Environment ** >( &m_cpp_env ), cpp.pData, nullptr ); + OSL_ENSURE( m_cpp_env.is(), "### cannot get C++ uno env!" ); + + uno_getMapping( + reinterpret_cast< uno_Mapping ** >( &m_uno2cpp ), + m_uno_env.get(), m_cpp_env.get(), nullptr ); + OSL_ENSURE( m_uno2cpp.is(), "### cannot get bridge uno <-> C++!" ); + + uno_getMapping( + reinterpret_cast< uno_Mapping ** >( &m_cpp2uno ), + m_cpp_env.get(), m_uno_env.get(), nullptr ); + OSL_ENSURE( m_cpp2uno.is(), "### cannot get bridge C++ <-> uno!" ); +} + +// XProxyFactory + +Reference< XAggregation > FactoryImpl::createProxy( + Reference< XInterface > const & xTarget ) +{ + return new ProxyRoot( this, xTarget ); +} + +// XServiceInfo + +OUString FactoryImpl::getImplementationName() +{ + return "com.sun.star.comp.reflection.ProxyFactory"; +} + +sal_Bool FactoryImpl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > FactoryImpl::getSupportedServiceNames() +{ + return { "com.sun.star.reflection.ProxyFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_FactoryImpl_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new FactoryImpl); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/access_controller.cxx b/stoc/source/security/access_controller.cxx new file mode 100644 index 0000000000..1359e529c7 --- /dev/null +++ b/stoc/source/security/access_controller.cxx @@ -0,0 +1,863 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <utility> +#include <vector> + +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <osl/thread.hxx> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <uno/current_context.h> +#include <uno/lbnames.h> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/uno/DeploymentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/security/XAccessController.hpp> +#include <com/sun/star/security/XPolicy.hpp> + +#include "lru_cache.h" +#include "permissions.h" + +#include <memory> + +constexpr OUString SERVICE_NAME = u"com.sun.star.security.AccessController"_ustr; +constexpr OUStringLiteral USER_CREDS = u"access-control.user-credentials.id"; + + +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; +using namespace stoc_sec; + +namespace { + +// static stuff initialized when loading lib +OUString s_envType = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +constexpr OUString s_acRestriction = u"access-control.restriction"_ustr; + + +/** ac context intersects permissions of two ac contexts +*/ +class acc_Intersection + : public WeakImplHelper< security::XAccessControlContext > +{ + Reference< security::XAccessControlContext > m_x1, m_x2; + + acc_Intersection( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + +public: + static Reference< security::XAccessControlContext > create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + + // XAccessControlContext impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; +}; + +acc_Intersection::acc_Intersection( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) + : m_x1( x1 ) + , m_x2( x2 ) +{} + +Reference< security::XAccessControlContext > acc_Intersection::create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) +{ + if (! x1.is()) + return x2; + if (! x2.is()) + return x1; + return new acc_Intersection( x1, x2 ); +} + +void acc_Intersection::checkPermission( + Any const & perm ) +{ + m_x1->checkPermission( perm ); + m_x2->checkPermission( perm ); +} + +/** ac context unifies permissions of two ac contexts +*/ +class acc_Union + : public WeakImplHelper< security::XAccessControlContext > +{ + Reference< security::XAccessControlContext > m_x1, m_x2; + + acc_Union( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + +public: + static Reference< security::XAccessControlContext > create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + + // XAccessControlContext impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; +}; + +acc_Union::acc_Union( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) + : m_x1( x1 ) + , m_x2( x2 ) +{} + +Reference< security::XAccessControlContext > acc_Union::create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) +{ + if (! x1.is()) + return Reference< security::XAccessControlContext >(); // unrestricted + if (! x2.is()) + return Reference< security::XAccessControlContext >(); // unrestricted + return new acc_Union( x1, x2 ); +} + +void acc_Union::checkPermission( + Any const & perm ) +{ + try + { + m_x1->checkPermission( perm ); + } + catch (security::AccessControlException &) + { + m_x2->checkPermission( perm ); + } +} + +/** ac context doing permission checks on static permissions +*/ +class acc_Policy + : public WeakImplHelper< security::XAccessControlContext > +{ + PermissionCollection m_permissions; + +public: + explicit acc_Policy( + PermissionCollection permissions ) + : m_permissions(std::move( permissions )) + {} + + // XAccessControlContext impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; +}; + +void acc_Policy::checkPermission( + Any const & perm ) +{ + m_permissions.checkPermission( perm ); +} + +/** current context overriding dynamic ac restriction +*/ +class acc_CurrentContext + : public WeakImplHelper< XCurrentContext > +{ + Reference< XCurrentContext > m_xDelegate; + Any m_restriction; + +public: + acc_CurrentContext( + Reference< XCurrentContext > const & xDelegate, + Reference< security::XAccessControlContext > const & xRestriction ); + + // XCurrentContext impl + virtual Any SAL_CALL getValueByName( OUString const & name ) override; +}; + +acc_CurrentContext::acc_CurrentContext( + Reference< XCurrentContext > const & xDelegate, + Reference< security::XAccessControlContext > const & xRestriction ) + : m_xDelegate( xDelegate ) +{ + if (xRestriction.is()) + { + m_restriction <<= xRestriction; + } + // return empty any otherwise on getValueByName(), not null interface +} + +Any acc_CurrentContext::getValueByName( OUString const & name ) +{ + if (name == s_acRestriction) + { + return m_restriction; + } + else if (m_xDelegate.is()) + { + return m_xDelegate->getValueByName( name ); + } + else + { + return Any(); + } +} + + +Reference< security::XAccessControlContext > getDynamicRestriction( + Reference< XCurrentContext > const & xContext ) +{ + if (xContext.is()) + { + Any acc(xContext->getValueByName(s_acRestriction)); + if (typelib_TypeClass_INTERFACE == acc.pType->eTypeClass) + { + // avoid ref-counting + OUString const & typeName = + OUString::unacquired( &acc.pType->pTypeName ); + if ( typeName == "com.sun.star.security.XAccessControlContext" ) + { + return Reference< security::XAccessControlContext >( + *static_cast< security::XAccessControlContext ** >( acc.pData ) ); + } + else // try to query + { + return Reference< security::XAccessControlContext >::query( + *static_cast< XInterface ** >( acc.pData ) ); + } + } + } + return Reference< security::XAccessControlContext >(); +} + +class cc_reset +{ + void * m_cc; +public: + explicit cc_reset( void * cc ) + : m_cc( cc ) {} + ~cc_reset() + { ::uno_setCurrentContext( m_cc, s_envType.pData, nullptr ); } +}; + +typedef WeakComponentImplHelper< + security::XAccessController, lang::XServiceInfo, lang::XInitialization > t_helper; + + +class AccessController + : public cppu::BaseMutex + , public t_helper +{ + Reference< XComponentContext > m_xComponentContext; + + Reference< security::XPolicy > m_xPolicy; + Reference< security::XPolicy > const & getPolicy(); + + // mode + enum class Mode { Off, On, DynamicOnly, SingleUser, SingleDefaultUser }; + Mode m_mode; + + PermissionCollection m_defaultPermissions; + // for single-user mode + PermissionCollection m_singleUserPermissions; + OUString m_singleUserId; + bool m_defaultPerm_init; + bool m_singleUser_init; + // for multi-user mode + lru_cache< OUString, PermissionCollection, OUStringHash, std::equal_to< OUString > > + m_user2permissions; + + ThreadData m_rec; + typedef std::vector< std::pair< OUString, Any > > t_rec_vec; + void clearPostPoned(); + void checkAndClearPostPoned(); + + PermissionCollection getEffectivePermissions( + Reference< XCurrentContext > const & xContext, + Any const & demanded_perm ); + +protected: + virtual void SAL_CALL disposing() override; + +public: + explicit AccessController( Reference< XComponentContext > const & xComponentContext ); + + // XInitialization impl + virtual void SAL_CALL initialize( + Sequence< Any > const & arguments ) override; + + // XAccessController impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; + virtual Any SAL_CALL doRestricted( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) override; + virtual Any SAL_CALL doPrivileged( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) override; + virtual Reference< security::XAccessControlContext > SAL_CALL getContext() override; + + // XServiceInfo impl + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +AccessController::AccessController( Reference< XComponentContext > const & xComponentContext ) + : t_helper( m_aMutex ) + , m_xComponentContext( xComponentContext ) + , m_mode( Mode::On ) // default + , m_defaultPerm_init( false ) + , m_singleUser_init( false ) + , m_rec( nullptr ) +{ + // The .../mode value had originally been set in + // cppu::add_access_control_entries (cppuhelper/source/servicefactory.cxx) + // to something other than "off" depending on various UNO_AC* bootstrap + // variables that are no longer supported, so this is mostly dead code now: + OUString mode; + if (m_xComponentContext->getValueByName( "/services/" + SERVICE_NAME + "/mode" ) >>= mode) + { + if ( mode == "off" ) + { + m_mode = Mode::Off; + } + else if ( mode == "on" ) + { + m_mode = Mode::On; + } + else if ( mode == "dynamic-only" ) + { + m_mode = Mode::DynamicOnly; + } + else if ( mode == "single-user" ) + { + m_xComponentContext->getValueByName( + "/services/" + SERVICE_NAME + "/single-user-id" ) >>= m_singleUserId; + if (m_singleUserId.isEmpty()) + { + throw RuntimeException( + "expected a user id in component context entry " + "\"/services/" + SERVICE_NAME + "/single-user-id\"!", + getXWeak() ); + } + m_mode = Mode::SingleUser; + } + else if ( mode == "single-default-user" ) + { + m_mode = Mode::SingleDefaultUser; + } + } + + // switch on caching for Mode::DynamicOnly and Mode::On (shareable multi-user process) + if (Mode::On != m_mode && Mode::DynamicOnly != m_mode) + return; + + sal_Int32 cacheSize = 0; // multi-user cache size + if (! (m_xComponentContext->getValueByName( + "/services/" + SERVICE_NAME + "/user-cache-size" ) >>= cacheSize)) + { + cacheSize = 128; // reasonable default? + } +#ifdef __CACHE_DIAGNOSE + cacheSize = 2; +#endif + m_user2permissions.setSize( cacheSize ); +} + +void AccessController::disposing() +{ + m_mode = Mode::Off; // avoid checks from now on xxx todo review/ better Mode::DynamicOnly? + m_xPolicy.clear(); + m_xComponentContext.clear(); +} + +// XInitialization impl + +void AccessController::initialize( + Sequence< Any > const & arguments ) +{ + // xxx todo: review for forking + // portal forking hack: re-initialize for another user-id + if (Mode::SingleUser != m_mode) // only if in single-user mode + { + throw RuntimeException( + "invalid call: ac must be in \"single-user\" mode!", getXWeak() ); + } + OUString userId; + arguments[ 0 ] >>= userId; + if ( userId.isEmpty() ) + { + throw RuntimeException( + "expected a user-id as first argument!", getXWeak() ); + } + // assured that no sync is necessary: no check happens at this forking time + m_singleUserId = userId; + m_singleUser_init = false; +} + + +Reference< security::XPolicy > const & AccessController::getPolicy() +{ + // get policy singleton + if (! m_xPolicy.is()) + { + Reference< security::XPolicy > xPolicy; + m_xComponentContext->getValueByName( + "/singletons/com.sun.star.security.thePolicy" ) >>= xPolicy; + if (!xPolicy.is()) + { + throw SecurityException( + "cannot get policy singleton!", getXWeak() ); + } + + MutexGuard guard( m_aMutex ); + if (! m_xPolicy.is()) + { + m_xPolicy = xPolicy; + } + } + return m_xPolicy; +} + +#ifdef __DIAGNOSE +static void dumpPermissions( + PermissionCollection const & collection, OUString const & userId = OUString() ) +{ + OUStringBuffer buf( 48 ); + if (!userId.isEmpty()) + { + buf.append( "> dumping permissions of user \"" ); + buf.append( userId ); + buf.append( "\":" ); + } + else + { + buf.append( "> dumping default permissions:" ); + } + SAL_INFO("stoc", buf.makeStringAndClear() ); + Sequence< OUString > permissions( collection.toStrings() ); + OUString const * p = permissions.getConstArray(); + for ( sal_Int32 nPos = 0; nPos < permissions.getLength(); ++nPos ) + { + SAL_INFO("stoc", p[ nPos ] ); + } + SAL_INFO("stoc", "> permission dump done" ); +} +#endif + + +void AccessController::clearPostPoned() +{ + delete static_cast< t_rec_vec * >( m_rec.getData() ); + m_rec.setData( nullptr ); +} + +void AccessController::checkAndClearPostPoned() +{ + // check postponed permissions + std::unique_ptr< t_rec_vec > rec( static_cast< t_rec_vec * >( m_rec.getData() ) ); + m_rec.setData( nullptr ); // takeover ownership + OSL_ASSERT(rec); + if (!rec) + return; + + t_rec_vec const& vec = *rec; + switch (m_mode) + { + case Mode::SingleUser: + { + OSL_ASSERT( m_singleUser_init ); + for (const auto & p : vec) + { + OSL_ASSERT( m_singleUserId == p.first ); + m_singleUserPermissions.checkPermission( p.second ); + } + break; + } + case Mode::SingleDefaultUser: + { + OSL_ASSERT( m_defaultPerm_init ); + for (const auto & p : vec) + { + OSL_ASSERT( p.first.isEmpty() ); // default-user + m_defaultPermissions.checkPermission( p.second ); + } + break; + } + case Mode::On: + { + for (const auto & p : vec) + { + PermissionCollection const * pPermissions; + // lookup policy for user + { + MutexGuard guard( m_aMutex ); + pPermissions = m_user2permissions.lookup( p.first ); + } + OSL_ASSERT( pPermissions ); + if (pPermissions) + { + pPermissions->checkPermission( p.second ); + } + } + break; + } + default: + OSL_FAIL( "### this should never be called in this ac mode!" ); + break; + } +} + +/** this is the only function calling the policy singleton and thus has to take care + of recurring calls! + + @param demanded_perm (if not empty) is the demanded permission of a checkPermission() call + which will be postponed for recurring calls +*/ +PermissionCollection AccessController::getEffectivePermissions( + Reference< XCurrentContext > const & xContext, + Any const & demanded_perm ) +{ + OUString userId; + + switch (m_mode) + { + case Mode::SingleUser: + { + if (m_singleUser_init) + return m_singleUserPermissions; + userId = m_singleUserId; + break; + } + case Mode::SingleDefaultUser: + { + if (m_defaultPerm_init) + return m_defaultPermissions; + break; + } + case Mode::On: + { + if (xContext.is()) + { + xContext->getValueByName( USER_CREDS ) >>= userId; + } + if ( userId.isEmpty() ) + { + throw SecurityException( + "cannot determine current user in multi-user ac!", getXWeak() ); + } + + // lookup policy for user + MutexGuard guard( m_aMutex ); + PermissionCollection const * pPermissions = m_user2permissions.lookup( userId ); + if (pPermissions) + return *pPermissions; + break; + } + default: + OSL_FAIL( "### this should never be called in this ac mode!" ); + return PermissionCollection(); + } + + // call on policy + // iff this is a recurring call for the default user, then grant all permissions + t_rec_vec * rec = static_cast< t_rec_vec * >( m_rec.getData() ); + if (rec) // tls entry exists => this is recursive call + { + if (demanded_perm.hasValue()) + { + // enqueue + rec->push_back( std::pair< OUString, Any >( userId, demanded_perm ) ); + } +#ifdef __DIAGNOSE + SAL_INFO("stoc", "> info: recurring call of user: " << userId ); +#endif + return PermissionCollection( new AllPermission() ); + } + else // no tls + { + rec = new t_rec_vec; + m_rec.setData( rec ); + } + + try // calls on API + { + // init default permissions + if (! m_defaultPerm_init) + { + PermissionCollection defaultPermissions( + getPolicy()->getDefaultPermissions() ); + // assign + MutexGuard guard( m_aMutex ); + if (! m_defaultPerm_init) + { + m_defaultPermissions = defaultPermissions; + m_defaultPerm_init = true; + } +#ifdef __DIAGNOSE + dumpPermissions( m_defaultPermissions ); +#endif + } + + PermissionCollection ret; + + // init user permissions + switch (m_mode) + { + case Mode::SingleUser: + { + ret = PermissionCollection( + getPolicy()->getPermissions( userId ), m_defaultPermissions ); + { + // assign + MutexGuard guard( m_aMutex ); + if (m_singleUser_init) + { + ret = m_singleUserPermissions; + } + else + { + m_singleUserPermissions = ret; + m_singleUser_init = true; + } + } +#ifdef __DIAGNOSE + dumpPermissions( ret, userId ); +#endif + break; + } + case Mode::SingleDefaultUser: + { + ret = m_defaultPermissions; + break; + } + case Mode::On: + { + ret = PermissionCollection( + getPolicy()->getPermissions( userId ), m_defaultPermissions ); + { + // cache + MutexGuard guard( m_aMutex ); + m_user2permissions.set( userId, ret ); + } +#ifdef __DIAGNOSE + dumpPermissions( ret, userId ); +#endif + break; + } + default: + break; + } + + // check postponed + checkAndClearPostPoned(); + return ret; + } + catch (const security::AccessControlException & exc) // wrapped into DeploymentException + { + clearPostPoned(); // safety: exception could have happened before checking postponed? + throw DeploymentException( "deployment error (AccessControlException occurred): " + exc.Message, exc.Context ); + } + catch (RuntimeException &) + { + // don't check postponed, just cleanup + clearPostPoned(); + delete static_cast< t_rec_vec * >( m_rec.getData() ); + m_rec.setData( nullptr ); + throw; + } + catch (Exception &) + { + // check postponed permissions first + // => AccessControlExceptions are errors, user exceptions not! + checkAndClearPostPoned(); + throw; + } + catch (...) + { + // don't check postponed, just cleanup + clearPostPoned(); + throw; + } +} + +// XAccessController impl + +void AccessController::checkPermission( + Any const & perm ) +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "checkPermission() call on disposed AccessController!", getXWeak() ); + } + + if (Mode::Off == m_mode) + return; + + // first dynamic check of ac contexts + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + Reference< security::XAccessControlContext > xACC( getDynamicRestriction( xContext ) ); + if (xACC.is()) + { + xACC->checkPermission( perm ); + } + + if (Mode::DynamicOnly == m_mode) + return; + + // then static check + getEffectivePermissions( xContext, perm ).checkPermission( perm ); +} + +Any AccessController::doRestricted( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "doRestricted() call on disposed AccessController!", getXWeak() ); + } + + if (Mode::Off == m_mode) // optimize this way, because no dynamic check will be performed + return xAction->run(); + + if (xRestriction.is()) + { + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + + // override restriction + Reference< XCurrentContext > xNewContext( + new acc_CurrentContext( xContext, acc_Intersection::create( + xRestriction, getDynamicRestriction( xContext ) ) ) ); + ::uno_setCurrentContext( xNewContext.get(), s_envType.pData, nullptr ); + cc_reset reset( xContext.get() ); + return xAction->run(); + } + else + { + return xAction->run(); + } +} + +Any AccessController::doPrivileged( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "doPrivileged() call on disposed AccessController!", getXWeak() ); + } + + if (Mode::Off == m_mode) // no dynamic check will be performed + { + return xAction->run(); + } + + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + + Reference< security::XAccessControlContext > xOldRestr( + getDynamicRestriction( xContext ) ); + + if (xOldRestr.is()) // previous restriction + { + // override restriction + Reference< XCurrentContext > xNewContext( + new acc_CurrentContext( xContext, acc_Union::create( xRestriction, xOldRestr ) ) ); + ::uno_setCurrentContext( xNewContext.get(), s_envType.pData, nullptr ); + cc_reset reset( xContext.get() ); + return xAction->run(); + } + else // no previous restriction => never current restriction + { + return xAction->run(); + } +} + +Reference< security::XAccessControlContext > AccessController::getContext() +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "getContext() call on disposed AccessController!", getXWeak() ); + } + + if (Mode::Off == m_mode) // optimize this way, because no dynamic check will be performed + { + return new acc_Policy( PermissionCollection( new AllPermission() ) ); + } + + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + + return acc_Intersection::create( + getDynamicRestriction( xContext ), + new acc_Policy( getEffectivePermissions( xContext, Any() ) ) ); +} + +// XServiceInfo impl + +OUString AccessController::getImplementationName() +{ + return "com.sun.star.security.comp.stoc.AccessController"; +} + +sal_Bool AccessController::supportsService( OUString const & serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +Sequence< OUString > AccessController::getSupportedServiceNames() +{ + Sequence<OUString> aSNS { SERVICE_NAME }; + return aSNS; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_security_comp_stoc_AccessController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new AccessController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/file_policy.cxx b/stoc/source/security/file_policy.cxx new file mode 100644 index 0000000000..63d0c6156e --- /dev/null +++ b/stoc/source/security/file_policy.cxx @@ -0,0 +1,494 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <osl/diagnose.h> +#include <osl/file.h> +#include <rtl/byteseq.hxx> +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/access_control.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/security/XPolicy.hpp> +#include <com/sun/star/security/AllPermission.hpp> +#include <com/sun/star/security/RuntimePermission.hpp> +#include <com/sun/star/io/FilePermission.hpp> +#include <com/sun/star/connection/SocketPermission.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <string_view> +#include <unordered_map> +#include <utility> + +constexpr OUString IMPL_NAME = u"com.sun.star.security.comp.stoc.FilePolicy"_ustr; + +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; + +namespace { + +typedef WeakComponentImplHelper< security::XPolicy, lang::XServiceInfo > t_helper; + + +class FilePolicy + : public cppu::BaseMutex + , public t_helper +{ + Reference< XComponentContext > m_xComponentContext; + AccessControl m_ac; + + Sequence< Any > m_defaultPermissions; + typedef std::unordered_map< OUString, Sequence< Any > > t_permissions; + t_permissions m_userPermissions; + bool m_init; + +protected: + virtual void SAL_CALL disposing() override; + +public: + explicit FilePolicy( Reference< XComponentContext > const & xComponentContext ); + + // XPolicy impl + virtual Sequence< Any > SAL_CALL getPermissions( + OUString const & userId ) override; + virtual Sequence< Any > SAL_CALL getDefaultPermissions() override; + virtual void SAL_CALL refresh() override; + + // XServiceInfo impl + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +FilePolicy::FilePolicy( Reference< XComponentContext > const & xComponentContext ) + : t_helper( m_aMutex ) + , m_xComponentContext( xComponentContext ) + , m_ac( xComponentContext ) + , m_init( false ) +{} + +void FilePolicy::disposing() +{ + m_userPermissions.clear(); + m_defaultPermissions = Sequence< Any >(); + m_xComponentContext.clear(); +} + + +Sequence< Any > FilePolicy::getPermissions( + OUString const & userId ) +{ + if (! m_init) + { + refresh(); + m_init = true; + } + + MutexGuard guard( m_aMutex ); + t_permissions::iterator iFind( m_userPermissions.find( userId ) ); + if (m_userPermissions.end() == iFind) + { + return Sequence< Any >(); + } + else + { + return iFind->second; + } +} + +Sequence< Any > FilePolicy::getDefaultPermissions() +{ + if (! m_init) + { + refresh(); + m_init = true; + } + + MutexGuard guard( m_aMutex ); + return m_defaultPermissions; +} + + +class PolicyReader +{ + OUString m_fileName; + oslFileHandle m_file; + + sal_Int32 m_linepos; + rtl::ByteSequence m_line; + sal_Int32 m_pos; + sal_Unicode m_back; + + sal_Unicode get(); + void back( sal_Unicode c ) + { m_back = c; } + + static bool isWhiteSpace( sal_Unicode c ) + { return (' ' == c || '\t' == c || '\n' == c || '\r' == c); } + void skipWhiteSpace(); + + static bool isCharToken( sal_Unicode c ) + { return (';' == c || ',' == c || '{' == c || '}' == c); } + +public: + PolicyReader( OUString file, AccessControl & ac ); + ~PolicyReader(); + + void error( std::u16string_view msg ); + + OUString getToken(); + OUString assureToken(); + OUString getQuotedToken(); + OUString assureQuotedToken(); + void assureToken( sal_Unicode token ); +}; + +void PolicyReader::assureToken( sal_Unicode token ) +{ + skipWhiteSpace(); + sal_Unicode c = get(); + if (c == token) + return; + OUString msg = "expected >" + OUStringChar(c) + "<!"; + error( msg ); +} + +OUString PolicyReader::assureQuotedToken() +{ + OUString token( getQuotedToken() ); + if (token.isEmpty()) + error( u"unexpected end of file!" ); + return token; +} + +OUString PolicyReader::getQuotedToken() +{ + skipWhiteSpace(); + OUStringBuffer buf( 32 ); + sal_Unicode c = get(); + if ('\"' != c) + error( u"expected quoting >\"< character!" ); + c = get(); + while ('\0' != c && '\"' != c) + { + buf.append( c ); + c = get(); + } + return buf.makeStringAndClear(); +} + +OUString PolicyReader::assureToken() +{ + OUString token( getToken() ); + if ( token.isEmpty()) + error( u"unexpected end of file!" ); + return token; +} + +OUString PolicyReader::getToken() +{ + skipWhiteSpace(); + sal_Unicode c = get(); + if (isCharToken( c )) + return OUString( &c, 1 ); + OUStringBuffer buf( 32 ); + while ('\0' != c && !isCharToken( c ) && !isWhiteSpace( c )) + { + buf.append( c ); + c = get(); + } + back( c ); + return buf.makeStringAndClear(); +} + +void PolicyReader::skipWhiteSpace() +{ + sal_Unicode c; + do + { + c = get(); + } + while (isWhiteSpace( c )); // seeking next non-whitespace char + + if ('/' == c) // C/C++ like comment + { + c = get(); + if ('/' == c) // C++ like comment + { + do + { + c = get(); + } + while ('\n' != c && '\0' != c); // seek eol/eof + skipWhiteSpace(); // cont skip on next line + } + else if ('*' == c) // C like comment + { + bool fini = true; + do + { + c = get(); + if ('*' == c) + { + c = get(); + fini = ('/' == c || '\0' == c); + } + else + { + fini = ('\0' == c); + } + } + while (! fini); + skipWhiteSpace(); // cont skip on next line + } + else + { + error( u"expected C/C++ like comment!" ); + } + } + else if ('#' == c) // script like comment + { + do + { + c = get(); + } + while ('\n' != c && '\0' != c); // seek eol/eof + skipWhiteSpace(); // cont skip on next line + } + + else // is token char + { + back( c ); + } +} + +sal_Unicode PolicyReader::get() +{ + if ('\0' != m_back) // one char push back possible + { + sal_Unicode c = m_back; + m_back = '\0'; + return c; + } + else if (m_pos == m_line.getLength()) // provide newline as whitespace + { + ++m_pos; + return '\n'; + } + else if (m_pos > m_line.getLength()) // read new line + { + sal_Bool eof; + oslFileError rc = ::osl_isEndOfFile( m_file, &eof ); + if (osl_File_E_None != rc) + error( u"checking eof failed!" ); + if (eof) + return '\0'; + + rc = ::osl_readLine( m_file, reinterpret_cast< sal_Sequence ** >( &m_line ) ); + if (osl_File_E_None != rc) + error( u"read line failed!" ); + ++m_linepos; + if (! m_line.getLength()) // empty line read + { + m_pos = 1; // read new line next time + return '\n'; + } + m_pos = 0; + } + return (m_line.getConstArray()[ m_pos++ ]); +} + +void PolicyReader::error( std::u16string_view msg ) +{ + throw RuntimeException( + "error processing file \"" + m_fileName + + "\" [line " + OUString::number(m_linepos) + + ", column " + OUString::number(m_pos) + + "] " + msg); +} + +PolicyReader::PolicyReader( OUString fileName, AccessControl & ac ) + : m_fileName(std::move( fileName )) + , m_linepos( 0 ) + , m_pos( 1 ) // force readline + , m_back( '\0' ) +{ + ac.checkFilePermission( m_fileName, "read" ); + if (osl_File_E_None != ::osl_openFile( m_fileName.pData, &m_file, osl_File_OpenFlag_Read )) + { + throw RuntimeException( "cannot open file \"" + m_fileName + "\"!" ); + } +} + +PolicyReader::~PolicyReader() +{ + if ( ::osl_closeFile( m_file ) != osl_File_E_None ) { + OSL_ASSERT( false ); + } +} + +constexpr OUStringLiteral s_grant = u"grant"; +constexpr OUStringLiteral s_user = u"user"; +constexpr OUStringLiteral s_permission = u"permission"; +constexpr OUStringLiteral s_openBrace = u"{"; +constexpr OUStringLiteral s_closingBrace = u"}"; + +constexpr OUStringLiteral s_filePermission = u"com.sun.star.io.FilePermission"; +constexpr OUStringLiteral s_socketPermission = u"com.sun.star.connection.SocketPermission"; +constexpr OUStringLiteral s_runtimePermission = u"com.sun.star.security.RuntimePermission"; +constexpr OUStringLiteral s_allPermission = u"com.sun.star.security.AllPermission"; + + +void FilePolicy::refresh() +{ + // read out file (the .../file-name value had originally been set in + // cppu::add_access_control_entries (cppuhelper/source/servicefactory.cxx) + // depending on various UNO_AC* bootstrap variables that are no longer + // supported, so this is effectively dead code): + OUString fileName; + m_xComponentContext->getValueByName( + "/implementations/" + IMPL_NAME + "/file-name" ) >>= fileName; + if ( fileName.isEmpty() ) + { + throw RuntimeException( + "name of policy file unknown!", + getXWeak() ); + } + + PolicyReader reader( fileName, m_ac ); + + // fill these two + Sequence< Any > defaultPermissions; + t_permissions userPermissions; + + OUString token( reader.getToken() ); + while (!token.isEmpty()) + { + if ( token != s_grant ) + reader.error( u"expected >grant< token!" ); + OUString userId; + token = reader.assureToken(); + if ( token == s_user ) // next token is user-id + { + userId = reader.assureQuotedToken(); + token = reader.assureToken(); + } + if ( token != s_openBrace ) + reader.error( u"expected opening brace >{<!" ); + token = reader.assureToken(); + // permissions list + while ( token != s_closingBrace ) + { + if ( token != s_permission ) + reader.error( u"expected >permission< or closing brace >}<!" ); + + token = reader.assureToken(); // permission type + Any perm; + if ( token == s_filePermission ) // FilePermission + { + OUString url( reader.assureQuotedToken() ); + reader.assureToken( ',' ); + OUString actions( reader.assureQuotedToken() ); + perm <<= io::FilePermission( url, actions ); + } + else if ( token == s_socketPermission ) // SocketPermission + { + OUString host( reader.assureQuotedToken() ); + reader.assureToken( ',' ); + OUString actions( reader.assureQuotedToken() ); + perm <<= connection::SocketPermission( host, actions ); + } + else if ( token == s_runtimePermission ) // RuntimePermission + { + OUString name( reader.assureQuotedToken() ); + perm <<= security::RuntimePermission( name ); + } + else if ( token == s_allPermission ) // AllPermission + { + perm <<= security::AllPermission(); + } + else + { + reader.error( u"expected permission type!" ); + } + + reader.assureToken( ';' ); + + // insert + if (!userId.isEmpty()) + { + Sequence< Any > perms( userPermissions[ userId ] ); + sal_Int32 len = perms.getLength(); + perms.realloc( len +1 ); + perms.getArray()[ len ] = perm; + userPermissions[ userId ] = perms; + } + else + { + sal_Int32 len = defaultPermissions.getLength(); + defaultPermissions.realloc( len +1 ); + defaultPermissions.getArray()[ len ] = perm; + } + + token = reader.assureToken(); // next permissions token + } + + reader.assureToken( ';' ); // semi + token = reader.getToken(); // next grant token + } + + // assign new ones + MutexGuard guard( m_aMutex ); + m_defaultPermissions = defaultPermissions; + m_userPermissions = userPermissions; +} + + +OUString FilePolicy::getImplementationName() +{ + return IMPL_NAME; +} + +sal_Bool FilePolicy::supportsService( OUString const & serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +Sequence< OUString > FilePolicy::getSupportedServiceNames() +{ + return { "com.sun.star.security.Policy" }; +} + +} // namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_security_comp_stoc_FilePolicy_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new FilePolicy(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/lru_cache.h b/stoc/source/security/lru_cache.h new file mode 100644 index 0000000000..402b41d587 --- /dev/null +++ b/stoc/source/security/lru_cache.h @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STOC_SOURCE_SECURITY_LRU_CACHE_H +#define INCLUDED_STOC_SOURCE_SECURITY_LRU_CACHE_H + +#include <memory> +#include <unordered_map> + +// __CACHE_DIAGNOSE works only for OUString keys +#ifdef __CACHE_DIAGNOSE +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#endif + + +namespace stoc_sec +{ + +/** Implementation of a least recently used (lru) cache. +*/ +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +class lru_cache +{ + struct Entry + { + t_key m_key; + t_val m_val; + Entry * m_pred; + Entry * m_succ; + }; + typedef std::unordered_map< t_key, Entry *, t_hashKey, t_equalKey > t_key2element; + t_key2element m_key2element; + ::std::size_t m_size; + + std::unique_ptr<Entry[]> m_block; + mutable Entry * m_head; + mutable Entry * m_tail; + inline void toFront( Entry * entry ) const; + +public: + /** Default Ctor. Does not cache. + */ + inline lru_cache(); + + /** Retrieves a pointer to value in cache. Returns 0, if none was found. + + @param key a key + @return pointer to value or 0 + */ + inline t_val const * lookup( t_key const & key ) const; + + /** Sets a value to be cached for given key. + + @param key a key + @param val a value + */ + inline void set( t_key const & key, t_val const & val ); + + /** Sets the number of elements to be cached. This will clear previous entries. + + @param cacheSize number of elements to be cached + */ + inline void setSize( ::std::size_t size ); +}; + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline void lru_cache< t_key, t_val, t_hashKey, t_equalKey >::setSize( + ::std::size_t size ) +{ + m_key2element.clear(); + m_block.reset(); + m_size = size; + + if (0 < m_size) + { + m_block.reset( new Entry[ m_size ] ); + m_head = m_block.get(); + m_tail = m_block.get() + m_size -1; + for ( ::std::size_t nPos = m_size; nPos--; ) + { + m_block[ nPos ].m_pred = m_block.get() + nPos -1; + m_block[ nPos ].m_succ = m_block.get() + nPos +1; + } + } +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline lru_cache< t_key, t_val, t_hashKey, t_equalKey >::lru_cache() + : m_size( 0 ) + , m_block( nullptr ) + , m_head( nullptr ) + , m_tail( nullptr ) +{ +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline void lru_cache< t_key, t_val, t_hashKey, t_equalKey >::toFront( + Entry * entry ) const +{ + if (entry != m_head) + { + // cut out element + if (entry == m_tail) + { + m_tail = entry->m_pred; + } + else + { + entry->m_succ->m_pred = entry->m_pred; + entry->m_pred->m_succ = entry->m_succ; + } + // push to front + m_head->m_pred = entry; + entry->m_succ = m_head; + m_head = entry; + } +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline t_val const * lru_cache< t_key, t_val, t_hashKey, t_equalKey >::lookup( + t_key const & key ) const +{ + if (0 < m_size) + { + typename t_key2element::const_iterator const iFind( m_key2element.find( key ) ); + if (iFind != m_key2element.end()) + { + Entry * entry = iFind->second; + toFront( entry ); +#ifdef __CACHE_DIAGNOSE + OUStringBuffer buf( 48 ); + buf.appendAscii( "> retrieved element \"" ); + buf.append( entry->m_key ); + buf.appendAscii( "\" from cache" ); + SAL_INFO("stoc", buf.makeStringAndClear() ); +#endif + return &entry->m_val; + } + } + return nullptr; +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline void lru_cache< t_key, t_val, t_hashKey, t_equalKey >::set( + t_key const & key, t_val const & val ) +{ + if (0 < m_size) + { + typename t_key2element::const_iterator const iFind( m_key2element.find( key ) ); + + Entry * entry; + if (iFind == m_key2element.end()) + { + entry = m_tail; // erase last element +#ifdef __CACHE_DIAGNOSE + if (entry->m_key.getLength()) + { + OUStringBuffer buf( 48 ); + buf.appendAscii( "> kicking element \"" ); + buf.append( entry->m_key ); + buf.appendAscii( "\" from cache" ); + SAL_INFO("stoc", buf.makeStringAndClear() ); + } +#endif + m_key2element.erase( entry->m_key ); + entry->m_key = key; + ::std::pair< typename t_key2element::iterator, bool > insertion( + m_key2element.emplace( key, entry ) ); + OSL_ENSURE( insertion.second, "### inserting new cache entry failed?!" ); + } + else + { + entry = iFind->second; +#ifdef __CACHE_DIAGNOSE + OUStringBuffer buf( 48 ); + buf.appendAscii( "> replacing element \"" ); + buf.append( entry->m_key ); + buf.appendAscii( "\" in cache" ); + SAL_INFO("stoc", buf.makeStringAndClear() ); +#endif + } + entry->m_val = val; + toFront( entry ); + } +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/permissions.cxx b/stoc/source/security/permissions.cxx new file mode 100644 index 0000000000..48a1f907f0 --- /dev/null +++ b/stoc/source/security/permissions.cxx @@ -0,0 +1,601 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <vector> + +#include <osl/process.h> +#include <osl/socket.hxx> +#include <osl/mutex.hxx> + +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <com/sun/star/security/RuntimePermission.hpp> +#include <com/sun/star/security/AllPermission.hpp> +#include <com/sun/star/io/FilePermission.hpp> +#include <com/sun/star/connection/SocketPermission.hpp> +#include <com/sun/star/security/AccessControlException.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include "permissions.h" + +using namespace ::osl; +using namespace ::com::sun::star; +using namespace css::uno; + +namespace stoc_sec +{ + + +static sal_Int32 makeMask( + OUString const & items, char const * const * strings ) +{ + sal_Int32 mask = 0; + + sal_Int32 n = 0; + do + { + OUString item( o3tl::trim(o3tl::getToken(items, 0, ',', n )) ); + if ( item.isEmpty()) + continue; + sal_Int32 nPos = 0; + while (strings[ nPos ]) + { + if (item.equalsAscii( strings[ nPos ] )) + { + mask |= (0x80000000 >> nPos); + break; + } + ++nPos; + } +#if OSL_DEBUG_LEVEL > 0 + if (! strings[ nPos ]) + { + SAL_WARN("stoc", "ignoring unknown socket action: " << item ); + } +#endif + } + while (n >= 0); // all items + return mask; +} + +static OUString makeStrings( + sal_Int32 mask, char const * const * strings ) +{ + OUStringBuffer buf( 48 ); + while (mask) + { + if (0x80000000 & mask) + { + buf.appendAscii( *strings ); + if ((mask << 1) != 0) // more items following + buf.append( ',' ); + } + mask = (mask << 1); + ++strings; + } + return buf.makeStringAndClear(); +} + +namespace { + +class SocketPermission : public Permission +{ + static char const * s_actions []; + sal_Int32 m_actions; + + OUString m_host; + sal_Int32 m_lowerPort; + sal_Int32 m_upperPort; + mutable OUString m_ip; + mutable bool m_resolveErr; + mutable bool m_resolvedHost; + bool m_wildCardHost; + + inline bool resolveHost() const; + +public: + SocketPermission( + connection::SocketPermission const & perm, + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ); + virtual bool implies( Permission const & perm ) const override; + virtual OUString toString() const override; +}; + +} + +char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", nullptr }; + +SocketPermission::SocketPermission( + connection::SocketPermission const & perm, + ::rtl::Reference< Permission > const & next ) + : Permission( SOCKET, next ) + , m_actions( makeMask( perm.Actions, s_actions ) ) + , m_host( perm.Host ) + , m_lowerPort( 0 ) + , m_upperPort( 65535 ) + , m_resolveErr( false ) + , m_resolvedHost( false ) + , m_wildCardHost( !perm.Host.isEmpty() && '*' == perm.Host.pData->buffer[ 0 ] ) +{ + if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied + m_actions |= 0x10000000; + + // separate host from portrange + sal_Int32 colon = m_host.indexOf( ':' ); + if (colon < 0) // port [range] not given + return; + + sal_Int32 minus = m_host.indexOf( '-', colon +1 ); + if (minus < 0) + { + m_lowerPort = m_upperPort = o3tl::toInt32(m_host.subView( colon +1 )); + } + else if (minus == (colon +1)) // -N + { + m_upperPort = o3tl::toInt32(m_host.subView( minus +1 )); + } + else if (minus == (m_host.getLength() -1)) // N- + { + m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, m_host.getLength() -1 -colon -1 )); + } + else // A-B + { + m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, minus - colon -1 )); + m_upperPort = o3tl::toInt32(m_host.subView( minus +1 )); + } + m_host = m_host.copy( 0, colon ); +} + +inline bool SocketPermission::resolveHost() const +{ + if (m_resolveErr) + return false; + + if (! m_resolvedHost) + { + // dns lookup + SocketAddr addr; + SocketAddr::resolveHostname( m_host, addr ); + OUString ip; + m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr( + addr.getHandle(), &ip.pData )); + if (m_resolveErr) + return false; + + MutexGuard guard( Mutex::getGlobalMutex() ); + if (! m_resolvedHost) + { + m_ip = ip; + m_resolvedHost = true; + } + } + return m_resolvedHost; +} + +bool SocketPermission::implies( Permission const & perm ) const +{ + // check type + if (SOCKET != perm.m_type) + return false; + SocketPermission const & demanded = static_cast< SocketPermission const & >( perm ); + + // check actions + if ((m_actions & demanded.m_actions) != demanded.m_actions) + return false; + + // check ports + if (demanded.m_lowerPort < m_lowerPort) + return false; + if (demanded.m_upperPort > m_upperPort) + return false; + + // quick check host (DNS names: RFC 1034/1035) + if (m_host.equalsIgnoreAsciiCase( demanded.m_host )) + return true; + // check for host wildcards + if (m_wildCardHost) + { + OUString const & demanded_host = demanded.m_host; + if (demanded_host.getLength() <= m_host.getLength()) + return false; + sal_Int32 len = m_host.getLength() -1; // skip star + return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( + demanded_host.getStr() + demanded_host.getLength() - len, len, + m_host.pData->buffer + 1, len )); + } + if (demanded.m_wildCardHost) + return false; + + // compare IP addresses + if (! resolveHost()) + return false; + if (! demanded.resolveHost()) + return false; + return m_ip == demanded.m_ip; +} + +OUString SocketPermission::toString() const +{ + OUStringBuffer buf( 48 ); + // host + buf.append( "com.sun.star.connection.SocketPermission (host=\"" + + m_host ); + if (m_resolvedHost) + { + buf.append( "[" + m_ip + "]" ); + } + // port + if (0 != m_lowerPort || 65535 != m_upperPort) + { + buf.append( ':' ); + if (m_lowerPort > 0) + buf.append( m_lowerPort ); + if (m_upperPort > m_lowerPort) + { + buf.append( '-' ); + if (m_upperPort < 65535) + buf.append( m_upperPort ); + } + } + // actions + buf.append( "\", actions=\"" + + makeStrings( m_actions, s_actions ) + + "\")" ); + return buf.makeStringAndClear(); +} + +namespace { + +class FilePermission : public Permission +{ + static char const * s_actions []; + sal_Int32 m_actions; + + OUString m_url; + bool m_allFiles; + +public: + FilePermission( + io::FilePermission const & perm, + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ); + virtual bool implies( Permission const & perm ) const override; + virtual OUString toString() const override; +}; + +} + +char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr }; + +static OUString const & getWorkingDir() +{ + static OUString s_workingDir = []() { + OUString workingDir; + ::osl_getProcessWorkingDir(&workingDir.pData); + return workingDir; + }(); + return s_workingDir; +} + +FilePermission::FilePermission( + io::FilePermission const & perm, + ::rtl::Reference< Permission > const & next ) + : Permission( FILE, next ) + , m_actions( makeMask( perm.Actions, s_actions ) ) + , m_url( perm.URL ) + , m_allFiles( perm.URL == "<<ALL FILES>>" ) +{ + if ( m_allFiles) + return; + + if ( m_url == "*" ) + { + m_url = getWorkingDir() + "/*"; + } + else if ( m_url == "-" ) + { + m_url = getWorkingDir() + "/-"; + } + else if (!m_url.startsWith("file:///")) + { + // relative path + OUString out; + oslFileError rc = ::osl_getAbsoluteFileURL( + getWorkingDir().pData, perm.URL.pData, &out.pData ); + m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback + } +#ifdef _WIN32 + // correct win drive letters + if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X| + { + constexpr OUStringLiteral s_colon = u":"; + // common case in API is a ':' (sal), so convert '|' to ':' + m_url = m_url.replaceAt( 9, 1, s_colon ); + } +#endif +} + +bool FilePermission::implies( Permission const & perm ) const +{ + // check type + if (FILE != perm.m_type) + return false; + FilePermission const & demanded = static_cast< FilePermission const & >( perm ); + + // check actions + if ((m_actions & demanded.m_actions) != demanded.m_actions) + return false; + + // check url + if (m_allFiles) + return true; + if (demanded.m_allFiles) + return false; + +#ifdef _WIN32 + if (m_url.equalsIgnoreAsciiCase( demanded.m_url )) + return true; +#else + if (m_url == demanded.m_url ) + return true; +#endif + if (m_url.getLength() > demanded.m_url.getLength()) + return false; + // check /- wildcard: all files and recursive in that path + if (m_url.endsWith("/-")) + { + // demanded url must start with granted path (including path trailing path sep) + sal_Int32 len = m_url.getLength() -1; +#ifdef _WIN32 + return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )); +#else + return (0 == ::rtl_ustr_reverseCompare_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )); +#endif + } + // check /* wildcard: all files in that path (not recursive!) + if (m_url.endsWith("/*")) + { + // demanded url must start with granted path (including path trailing path sep) + sal_Int32 len = m_url.getLength() -1; +#ifdef _WIN32 + return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) && + (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths +#else + return ((0 == ::rtl_ustr_reverseCompare_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) && + (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths +#endif + } + return false; +} + +OUString FilePermission::toString() const +{ + return + // url + "com.sun.star.io.FilePermission (url=\"" + m_url + // actions + + "\", actions=\"" + makeStrings( m_actions, s_actions ) + "\")"; +} + +namespace { + +class RuntimePermission : public Permission +{ + OUString m_name; + +public: + RuntimePermission( + security::RuntimePermission const & perm, + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) + : Permission( RUNTIME, next ) + , m_name( perm.Name ) + {} + virtual bool implies( Permission const & perm ) const override; + virtual OUString toString() const override; +}; + +} + +bool RuntimePermission::implies( Permission const & perm ) const +{ + // check type + if (RUNTIME != perm.m_type) + return false; + RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm ); + + // check name + return m_name == demanded.m_name; +} + +OUString RuntimePermission::toString() const +{ + return "com.sun.star.security.RuntimePermission (name=\"" + + m_name + "\")"; +} + + +bool AllPermission::implies( Permission const & ) const +{ + return true; +} + +OUString AllPermission::toString() const +{ + return "com.sun.star.security.AllPermission"; +} + + +PermissionCollection::PermissionCollection( + Sequence< Any > const & permissions, PermissionCollection const & addition ) + : m_head( addition.m_head ) +{ + Any const * perms = permissions.getConstArray(); + for ( sal_Int32 nPos = permissions.getLength(); nPos--; ) + { + Any const & perm = perms[ nPos ]; + Type const & perm_type = perm.getValueType(); + + // supported permission types + if (perm_type.equals( cppu::UnoType<io::FilePermission>::get())) + { + m_head = new FilePermission( + *static_cast< io::FilePermission const * >( perm.pData ), m_head ); + } + else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get())) + { + m_head = new SocketPermission( + *static_cast< connection::SocketPermission const * >( perm.pData ), m_head ); + } + else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get())) + { + m_head = new RuntimePermission( + *static_cast< security::RuntimePermission const * >( perm.pData ), m_head ); + } + else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get())) + { + m_head = new AllPermission( m_head ); + } + else + { + throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() ); + } + } +} +#ifdef __DIAGNOSE + +Sequence< OUString > PermissionCollection::toStrings() const +{ + std::vector< OUString > strings; + strings.reserve( 8 ); + for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() ) + { + strings.push_back( perm->toString() ); + } + return Sequence< OUString >( strings.data(), strings.size() ); +} +#endif + +static bool implies( + ::rtl::Reference< Permission > const & head, Permission const & demanded ) +{ + for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() ) + { + if (perm->implies( demanded )) + return true; + } + return false; +} + +#ifdef __DIAGNOSE + +static void demanded_diag( + Permission const & perm ) +{ + OUStringBuffer buf( 48 ); + buf.append( "demanding " ); + buf.append( perm.toString() ); + buf.append( " => ok." ); + OString str( + OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) ); + SAL_INFO("stoc",( "%s", str.getStr() ); +} +#endif + +static void throwAccessControlException( + Permission const & perm, Any const & demanded_perm ) +{ + throw security::AccessControlException( + "access denied: " + perm.toString(), + Reference< XInterface >(), demanded_perm ); +} + +void PermissionCollection::checkPermission( Any const & perm ) const +{ + Type const & demanded_type = perm.getValueType(); + + // supported permission types + // stack object of SimpleReferenceObject are ok, as long as they are not + // assigned to a ::rtl::Reference<> (=> delete this) + if (demanded_type.equals( cppu::UnoType<io::FilePermission>::get())) + { + FilePermission demanded( + *static_cast< io::FilePermission const * >( perm.pData ) ); + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get())) + { + SocketPermission demanded( + *static_cast< connection::SocketPermission const * >( perm.pData ) ); + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get())) + { + RuntimePermission demanded( + *static_cast< security::RuntimePermission const * >( perm.pData ) ); + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get())) + { + AllPermission demanded; + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else + { + throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/permissions.h b/stoc/source/security/permissions.h new file mode 100644 index 0000000000..99c5584635 --- /dev/null +++ b/stoc/source/security/permissions.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_STOC_SOURCE_SECURITY_PERMISSIONS_H +#define INCLUDED_STOC_SOURCE_SECURITY_PERMISSIONS_H + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <utility> + +namespace com::sun::star::uno { class Any; } +namespace com::sun::star::uno { template <class E> class Sequence; } + +namespace stoc_sec +{ + +class Permission : public ::salhelper::SimpleReferenceObject +{ +public: + ::rtl::Reference< Permission > m_next; + // mode + enum t_type { ALL, RUNTIME, SOCKET, FILE } m_type; + + Permission( + t_type type, + ::rtl::Reference< Permission > next ) + : m_next(std::move( next )) + , m_type( type ) + {} + + virtual bool implies( Permission const & perm ) const = 0; + virtual OUString toString() const = 0; +}; + +class AllPermission : public Permission +{ +public: + explicit AllPermission( + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) + : Permission( ALL, next ) + {} + + virtual bool implies( Permission const & ) const override; + virtual OUString toString() const override; +}; + + +class PermissionCollection +{ + ::rtl::Reference< Permission > m_head; +public: + PermissionCollection() + {} + explicit PermissionCollection( ::rtl::Reference< Permission > single ) + : m_head(std::move( single )) + {} + PermissionCollection( + css::uno::Sequence< css::uno::Any > const & permissions, + PermissionCollection const & addition = PermissionCollection() ); +#ifdef __DIAGNOSE + css::uno::Sequence< OUString > toStrings() const; +#endif + void checkPermission( css::uno::Any const & perm ) const; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/servicemanager/servicemanager.cxx b/stoc/source/servicemanager/servicemanager.cxx new file mode 100644 index 0000000000..c61e23cf85 --- /dev/null +++ b/stoc/source/servicemanager/servicemanager.cxx @@ -0,0 +1,1476 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <o3tl/any.hxx> +#include <osl/mutex.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/weakref.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/container/XSet.hpp> +#include <com/sun/star/container/XElementAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <iterator> +#include <mutex> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <utility> + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::beans; +using namespace css::registry; +using namespace css::lang; +using namespace css::container; +using namespace cppu; +using namespace osl; + +namespace { + +Sequence< OUString > retrieveAsciiValueList( + const Reference< XSimpleRegistry > &xReg, const OUString &keyName ) +{ + Reference< XEnumerationAccess > xAccess( xReg, UNO_QUERY ); + Sequence< OUString > seq; + if( xAccess.is() ) + { + Reference< XEnumeration > xEnum = xAccess->createEnumeration(); + while( xEnum.is() && xEnum->hasMoreElements() ) + { + Reference< XSimpleRegistry > xTempReg; + xEnum->nextElement() >>= xTempReg; + if( xTempReg.is() ) + { + const Sequence< OUString > seq2 = retrieveAsciiValueList( xTempReg, keyName ); + + if( seq2.hasElements() ) + { + sal_Int32 n1Len = seq.getLength(); + sal_Int32 n2Len = seq2.getLength(); + + seq.realloc( n1Len + n2Len ); + std::copy(seq2.begin(), seq2.end(), std::next(seq.getArray(), n1Len)); + } + } + } + } + else if( xReg.is () ) + { + try + { + Reference< XRegistryKey > rRootKey = xReg->getRootKey(); + if( rRootKey.is() ) + { + Reference<XRegistryKey > xKey = rRootKey->openKey(keyName); + if( xKey.is() ) + { + seq = xKey->getAsciiListValue(); + } + } + } + catch( InvalidRegistryException & ) + { + } + catch (InvalidValueException &) + { + } + } + return seq; +} + +/***************************************************************************** + Enumeration by ServiceName +*****************************************************************************/ + +typedef std::unordered_set< Reference<XInterface > > HashSet_Ref; + + +class ServiceEnumeration_Impl : public WeakImplHelper< XEnumeration > +{ +public: + explicit ServiceEnumeration_Impl( const Sequence< Reference<XInterface > > & rFactories ) + : aFactories( rFactories ) + , nIt( 0 ) + {} + + // XEnumeration + sal_Bool SAL_CALL hasMoreElements() override; + Any SAL_CALL nextElement() override; +private: + std::mutex aMutex; + Sequence< Reference<XInterface > > aFactories; + sal_Int32 nIt; +}; + +// XEnumeration +sal_Bool ServiceEnumeration_Impl::hasMoreElements() +{ + std::scoped_lock aGuard( aMutex ); + return nIt != aFactories.getLength(); +} + +// XEnumeration +Any ServiceEnumeration_Impl::nextElement() +{ + std::scoped_lock aGuard( aMutex ); + if( nIt == aFactories.getLength() ) + throw NoSuchElementException("no more elements"); + + return Any( &aFactories.getConstArray()[nIt++], cppu::UnoType<XInterface>::get()); +} + + +class PropertySetInfo_Impl : public WeakImplHelper< beans::XPropertySetInfo > +{ + Sequence< beans::Property > m_properties; + +public: + explicit PropertySetInfo_Impl( Sequence< beans::Property > const & properties ) + : m_properties( properties ) + {} + + // XPropertySetInfo impl + virtual Sequence< beans::Property > SAL_CALL getProperties() override; + virtual beans::Property SAL_CALL getPropertyByName( OUString const & name ) override; + virtual sal_Bool SAL_CALL hasPropertyByName( OUString const & name ) override; +}; + +Sequence< beans::Property > PropertySetInfo_Impl::getProperties() +{ + return m_properties; +} + +beans::Property PropertySetInfo_Impl::getPropertyByName( OUString const & name ) +{ + beans::Property const * p = m_properties.getConstArray(); + for ( sal_Int32 nPos = m_properties.getLength(); nPos--; ) + { + if (p[ nPos ].Name == name) + return p[ nPos ]; + } + throw beans::UnknownPropertyException( + "unknown property: " + name ); +} + +sal_Bool PropertySetInfo_Impl::hasPropertyByName( OUString const & name ) +{ + return std::any_of(std::cbegin(m_properties), std::cend(m_properties), + [&name](const beans::Property& rProp) { return rProp.Name == name; }); +} + + +/***************************************************************************** + Enumeration by implementation +*****************************************************************************/ +class ImplementationEnumeration_Impl : public WeakImplHelper< XEnumeration > +{ +public: + explicit ImplementationEnumeration_Impl( HashSet_Ref xImplementationMap ) + : aImplementationMap(std::move( xImplementationMap )) + , aIt( aImplementationMap.begin() ) + {} + + // XEnumeration + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual Any SAL_CALL nextElement() override; + +private: + std::mutex aMutex; + HashSet_Ref aImplementationMap; + HashSet_Ref::iterator aIt; +}; + +// XEnumeration +sal_Bool ImplementationEnumeration_Impl::hasMoreElements() +{ + std::scoped_lock aGuard( aMutex ); + return aIt != aImplementationMap.end(); +} + +// XEnumeration +Any ImplementationEnumeration_Impl::nextElement() +{ + std::scoped_lock aGuard( aMutex ); + if( aIt == aImplementationMap.end() ) + throw NoSuchElementException("no more elements"); + + Any ret( &(*aIt), cppu::UnoType<XInterface>::get()); + ++aIt; + return ret; +} + +/***************************************************************************** + Hash tables +*****************************************************************************/ +typedef std::unordered_set +< + OUString +> HashSet_OWString; + +typedef std::unordered_multimap +< + OUString, + Reference<XInterface > +> HashMultimap_OWString_Interface; + +typedef std::unordered_map +< + OUString, + Reference<XInterface > +> HashMap_OWString_Interface; + +/***************************************************************************** + class OServiceManager_Listener +*****************************************************************************/ +class OServiceManager_Listener : public WeakImplHelper< XEventListener > +{ +private: + WeakReference<XSet > xSMgr; + +public: + explicit OServiceManager_Listener( const Reference<XSet > & rSMgr ) + : xSMgr( rSMgr ) + {} + + // XEventListener + virtual void SAL_CALL disposing(const EventObject & rEvt ) override; +}; + +void OServiceManager_Listener::disposing(const EventObject & rEvt ) +{ + Reference<XSet > x( xSMgr ); + if( !x.is() ) + return; + + try + { + x->remove( Any( &rEvt.Source, cppu::UnoType<XInterface>::get()) ); + } + catch( const IllegalArgumentException & ) + { + OSL_FAIL( "IllegalArgumentException caught" ); + } + catch( const NoSuchElementException & ) + { + OSL_FAIL( "NoSuchElementException caught" ); + } +} + + +/***************************************************************************** + class OServiceManager +*****************************************************************************/ + +typedef WeakComponentImplHelper< + lang::XMultiServiceFactory, lang::XMultiComponentFactory, lang::XServiceInfo, + lang::XInitialization, + container::XSet, container::XContentEnumerationAccess, + beans::XPropertySet > t_OServiceManager_impl; + +class OServiceManager + : public cppu::BaseMutex + , public t_OServiceManager_impl +{ +public: + explicit OServiceManager( Reference< XComponentContext > const & xContext ); + + // XInitialization + void SAL_CALL initialize( Sequence< Any > const & args ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XMultiComponentFactory + virtual Reference< XInterface > SAL_CALL createInstanceWithContext( + OUString const & rServiceSpecifier, Reference< XComponentContext > const & xContext ) override; + virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext( + OUString const & rServiceSpecifier, + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) override; +// virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() +// throw (RuntimeException); + + // XMultiServiceFactory + virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + virtual Reference<XInterface > SAL_CALL createInstance(const OUString &) override; + virtual Reference<XInterface > SAL_CALL createInstanceWithArguments(const OUString &, const Sequence<Any >& Arguments) override; + + // The same as the getAvailableServiceNames, but only unique names + Sequence< OUString > getUniqueAvailableServiceNames( + HashSet_OWString & aNameSet ); + + // XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XEnumerationAccess + virtual Reference<XEnumeration > SAL_CALL createEnumeration() override; + + // XSet + virtual sal_Bool SAL_CALL has( const Any & Element ) override; + virtual void SAL_CALL insert( const Any & Element ) override; + virtual void SAL_CALL remove( const Any & Element ) override; + + // XContentEnumerationAccess + //Sequence< OUString > getAvailableServiceNames() throw( (Exception) ); + virtual Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XPropertySet + Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override; + Any SAL_CALL getPropertyValue(const OUString& PropertyName) override; + void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override; + void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override; + void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override; + void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override; + +protected: + bool is_disposed() const; + void check_undisposed() const; + virtual void SAL_CALL disposing() override; + + bool haveFactoryWithThisImplementation(const OUString& aImplName); + + virtual Sequence< Reference< XInterface > > queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & xContext ); + + Reference< XComponentContext > m_xContext; + + Reference< beans::XPropertySetInfo > m_xPropertyInfo; + + // factories which have been loaded and not inserted( by XSet::insert) + // are remembered by this set. + HashSet_Ref m_SetLoadedFactories; +private: + + Reference<XEventListener > getFactoryListener(); + + + HashMultimap_OWString_Interface m_ServiceMap; + HashSet_Ref m_ImplementationMap; + HashMap_OWString_Interface m_ImplementationNameMap; + Reference<XEventListener > xFactoryListener; + bool m_bInDisposing; +}; + + +bool OServiceManager::is_disposed() const +{ + // ought to be guarded by m_mutex: + return (m_bInDisposing || rBHelper.bDisposed); +} + + +void OServiceManager::check_undisposed() const +{ + if (is_disposed()) + { + throw lang::DisposedException( + "service manager instance has already been disposed!", + const_cast<OServiceManager *>(this)->getXWeak() ); + } +} + + +typedef WeakComponentImplHelper< + lang::XMultiServiceFactory, lang::XMultiComponentFactory, lang::XServiceInfo, + container::XSet, container::XContentEnumerationAccess, + beans::XPropertySet > t_OServiceManagerWrapper_impl; + +class OServiceManagerWrapper : public cppu::BaseMutex, public t_OServiceManagerWrapper_impl +{ + Reference< XComponentContext > m_xContext; + Reference< XMultiComponentFactory > m_root; + Reference< XMultiComponentFactory > const & getRoot() const + { + if (! m_root.is()) + { + throw lang::DisposedException( + "service manager instance has already been disposed!" ); + } + return m_root; + } + +protected: + virtual void SAL_CALL disposing() override; + +public: + explicit OServiceManagerWrapper( + Reference< XComponentContext > const & xContext ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->getImplementationName(); } + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override + { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->supportsService( ServiceName ); } + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override + { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->getSupportedServiceNames(); } + + // XMultiComponentFactory + virtual Reference< XInterface > SAL_CALL createInstanceWithContext( + OUString const & rServiceSpecifier, Reference< XComponentContext > const & xContext ) override + { return getRoot()->createInstanceWithContext( rServiceSpecifier, xContext ); } + virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext( + OUString const & rServiceSpecifier, + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) override + { return getRoot()->createInstanceWithArgumentsAndContext( rServiceSpecifier, rArguments, xContext ); } +// virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() +// throw (RuntimeException); + + // XMultiServiceFactory + virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() override + { return getRoot()->getAvailableServiceNames(); } + virtual Reference<XInterface > SAL_CALL createInstance(const OUString & name) override + { return getRoot()->createInstanceWithContext( name, m_xContext ); } + virtual Reference<XInterface > SAL_CALL createInstanceWithArguments(const OUString & name, const Sequence<Any >& Arguments) override + { return getRoot()->createInstanceWithArgumentsAndContext( name, Arguments, m_xContext ); } + + // XElementAccess + virtual Type SAL_CALL getElementType() override + { return Reference< XElementAccess >(getRoot(), UNO_QUERY_THROW)->getElementType(); } + virtual sal_Bool SAL_CALL hasElements() override + { return Reference< XElementAccess >(getRoot(), UNO_QUERY_THROW)->hasElements(); } + + // XEnumerationAccess + virtual Reference<XEnumeration > SAL_CALL createEnumeration() override + { return Reference< XEnumerationAccess >(getRoot(), UNO_QUERY_THROW)->createEnumeration(); } + + // XSet + virtual sal_Bool SAL_CALL has( const Any & Element ) override + { return Reference< XSet >(getRoot(), UNO_QUERY_THROW)->has( Element ); } + virtual void SAL_CALL insert( const Any & Element ) override + { Reference< XSet >(getRoot(), UNO_QUERY_THROW)->insert( Element ); } + virtual void SAL_CALL remove( const Any & Element ) override + { Reference< XSet >(getRoot(), UNO_QUERY_THROW)->remove( Element ); } + + // XContentEnumerationAccess + //Sequence< OUString > getAvailableServiceNames() throw( (Exception) ); + virtual Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override + { return Reference< XContentEnumerationAccess >(getRoot(), UNO_QUERY_THROW)->createContentEnumeration( aServiceName ); } + + // XPropertySet + Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override + { return Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->getPropertySetInfo(); } + + void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override; + Any SAL_CALL getPropertyValue(const OUString& PropertyName) override; + + void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->addPropertyChangeListener( PropertyName, aListener ); } + void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->removePropertyChangeListener( PropertyName, aListener ); } + void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->addVetoableChangeListener( PropertyName, aListener ); } + void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->removeVetoableChangeListener( PropertyName, aListener ); } +}; + +void SAL_CALL OServiceManagerWrapper::setPropertyValue( + const OUString& PropertyName, const Any& aValue ) +{ + if ( PropertyName == "DefaultContext" ) + { + Reference< XComponentContext > xContext; + if (!(aValue >>= xContext)) + { + throw IllegalArgumentException( + "no XComponentContext given!", + getXWeak(), 1 ); + } + + MutexGuard aGuard( m_aMutex ); + m_xContext = xContext; + + } + else + { + Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->setPropertyValue( PropertyName, aValue ); + } +} + +Any SAL_CALL OServiceManagerWrapper::getPropertyValue( + const OUString& PropertyName ) +{ + if ( PropertyName == "DefaultContext" ) + { + MutexGuard aGuard( m_aMutex ); + if( m_xContext.is() ) + return Any( m_xContext ); + else + return Any(); + } + else + { + return Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->getPropertyValue( PropertyName ); + } +} + +void OServiceManagerWrapper::disposing() +{ + m_xContext.clear(); + +// no m_root->dispose(), because every context disposes its service manager... + m_root.clear(); +} + +OServiceManagerWrapper::OServiceManagerWrapper( + Reference< XComponentContext > const & xContext ) + : t_OServiceManagerWrapper_impl( m_aMutex ) + , m_xContext( xContext ) + , m_root( xContext->getServiceManager() ) +{ + if (! m_root.is()) + { + throw RuntimeException( + "no service manager to wrap" ); + } +} + + +/** + * Create a ServiceManager + */ +OServiceManager::OServiceManager( Reference< XComponentContext > const & xContext ) + : t_OServiceManager_impl( m_aMutex ) + , m_xContext( xContext ) + , m_bInDisposing( false ) +{} + +// XComponent +void OServiceManager::dispose() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + t_OServiceManager_impl::dispose(); +} + +void OServiceManager::disposing() +{ + // dispose all factories + HashSet_Ref aImpls; + { + MutexGuard aGuard( m_aMutex ); + m_bInDisposing = true; + aImpls = m_ImplementationMap; + } + for( const auto& rxImpl : aImpls ) + { + try + { + Reference<XComponent > xComp( Reference<XComponent >::query( rxImpl ) ); + if( xComp.is() ) + xComp->dispose(); + } + catch (const RuntimeException & exc) + { + SAL_INFO("stoc", "RuntimeException occurred upon disposing factory: " << exc); + } + } + + // dispose + HashSet_Ref aImplMap; + { + MutexGuard aGuard( m_aMutex ); + // erase all members + m_ServiceMap = HashMultimap_OWString_Interface(); + aImplMap = m_ImplementationMap; + m_ImplementationMap = HashSet_Ref(); + m_ImplementationNameMap = HashMap_OWString_Interface(); + m_SetLoadedFactories= HashSet_Ref(); + } + + m_xContext.clear(); + + // not only the Event should hold the object + OSL_ASSERT( m_refCount != 1 ); +} + +// XPropertySet +Reference<XPropertySetInfo > OServiceManager::getPropertySetInfo() +{ + check_undisposed(); + if (! m_xPropertyInfo.is()) + { + Sequence< beans::Property > seq{ beans::Property( + "DefaultContext", -1, cppu::UnoType<decltype(m_xContext)>::get(), 0 ) }; + Reference< beans::XPropertySetInfo > xInfo( new PropertySetInfo_Impl( seq ) ); + + MutexGuard aGuard( m_aMutex ); + if (! m_xPropertyInfo.is()) + { + m_xPropertyInfo = xInfo; + } + } + return m_xPropertyInfo; +} + +void OServiceManager::setPropertyValue( + const OUString& PropertyName, const Any& aValue ) +{ + check_undisposed(); + if ( PropertyName != "DefaultContext" ) + { + throw UnknownPropertyException( + "unknown property " + PropertyName, + getXWeak() ); + } + + Reference< XComponentContext > xContext; + if (!(aValue >>= xContext)) + { + throw IllegalArgumentException( + "no XComponentContext given!", + getXWeak(), 1 ); + } + + MutexGuard aGuard( m_aMutex ); + m_xContext = xContext; +} + +Any OServiceManager::getPropertyValue(const OUString& PropertyName) +{ + check_undisposed(); + if ( PropertyName == "DefaultContext" ) + { + MutexGuard aGuard( m_aMutex ); + if( m_xContext.is() ) + return Any( m_xContext ); + else + return Any(); + } + else + { + UnknownPropertyException except("ServiceManager : unknown property " + PropertyName, {}); + throw except; + } +} + +void OServiceManager::addPropertyChangeListener( + const OUString&, const Reference<XPropertyChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +void OServiceManager::removePropertyChangeListener( + const OUString&, const Reference<XPropertyChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +void OServiceManager::addVetoableChangeListener( + const OUString&, const Reference<XVetoableChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +void OServiceManager::removeVetoableChangeListener( + const OUString&, const Reference<XVetoableChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +// OServiceManager +Reference<XEventListener > OServiceManager::getFactoryListener() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + if( !xFactoryListener.is() ) + xFactoryListener = new OServiceManager_Listener( this ); + return xFactoryListener; +} + +// XMultiServiceFactory, XContentEnumeration +Sequence< OUString > OServiceManager::getUniqueAvailableServiceNames( + HashSet_OWString & aNameSet ) +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + for( const auto& rEntry : m_ServiceMap ) + aNameSet.insert( rEntry.first ); + + /* do not return the implementation names + HashMap_OWString_Interface m_ImplementationNameMap; + HashMap_OWString_Interface::iterator aIt = m_ImplementationNameMap.begin(); + while( aIt != m_ImplementationNameMap.end() ) + aNameSet.insert( (*aIt++).first ); + */ + + return comphelper::containerToSequence(aNameSet); +} + +// XMultiComponentFactory +Reference< XInterface > OServiceManager::createInstanceWithContext( + OUString const & rServiceSpecifier, + Reference< XComponentContext > const & xContext ) +{ + check_undisposed(); +#if OSL_DEBUG_LEVEL > 0 + Reference< beans::XPropertySet > xProps( xContext->getServiceManager(), UNO_QUERY ); + OSL_ASSERT( xProps.is() ); + if (xProps.is()) + { + Reference< XComponentContext > xDefContext; + xProps->getPropertyValue( "DefaultContext" ) >>= xDefContext; + OSL_ENSURE( + xContext == xDefContext, + "### default context of service manager singleton differs from context holding it!" ); + } +#endif + + const Sequence< Reference< XInterface > > factories( + queryServiceFactories( rServiceSpecifier, xContext ) ); + for ( Reference< XInterface > const & xFactory : factories ) + { + try + { + if (xFactory.is()) + { + Reference< XSingleComponentFactory > xFac( xFactory, UNO_QUERY ); + if (xFac.is()) + { + return xFac->createInstanceWithContext( xContext ); + } + else + { + Reference< XSingleServiceFactory > xFac2( xFactory, UNO_QUERY ); + if (xFac2.is()) + { + SAL_INFO("stoc", "ignoring given context raising service " << rServiceSpecifier << "!!!"); + return xFac2->createInstance(); + } + } + } + } + catch (const lang::DisposedException & exc) + { + SAL_INFO("stoc", "DisposedException occurred: " << exc); + } + } + + return Reference< XInterface >(); +} +// XMultiComponentFactory +Reference< XInterface > OServiceManager::createInstanceWithArgumentsAndContext( + OUString const & rServiceSpecifier, + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) +{ + check_undisposed(); +#if OSL_DEBUG_LEVEL > 0 + Reference< beans::XPropertySet > xProps( xContext->getServiceManager(), UNO_QUERY ); + OSL_ASSERT( xProps.is() ); + if (xProps.is()) + { + Reference< XComponentContext > xDefContext; + xProps->getPropertyValue( "DefaultContext" ) >>= xDefContext; + OSL_ENSURE( + xContext == xDefContext, + "### default context of service manager singleton differs from context holding it!" ); + } +#endif + + const Sequence< Reference< XInterface > > factories( + queryServiceFactories( rServiceSpecifier, xContext ) ); + for ( Reference< XInterface > const & xFactory : factories ) + { + try + { + if (xFactory.is()) + { + Reference< XSingleComponentFactory > xFac( xFactory, UNO_QUERY ); + if (xFac.is()) + { + return xFac->createInstanceWithArgumentsAndContext( rArguments, xContext ); + } + else + { + Reference< XSingleServiceFactory > xFac2( xFactory, UNO_QUERY ); + if (xFac2.is()) + { + SAL_INFO("stoc", "ignoring given context raising service " << rServiceSpecifier << "!!!"); + return xFac2->createInstanceWithArguments( rArguments ); + } + } + } + } + catch (const lang::DisposedException & exc) + { + SAL_INFO("stoc", "DisposedException occurred: " << exc); + } + } + + return Reference< XInterface >(); +} + +// XMultiServiceFactory, XMultiComponentFactory, XContentEnumeration +Sequence< OUString > OServiceManager::getAvailableServiceNames() +{ + check_undisposed(); + // all names + HashSet_OWString aNameSet; + return getUniqueAvailableServiceNames( aNameSet ); +} + +// XMultipleServiceFactory +Reference<XInterface > OServiceManager::createInstance( + const OUString& rServiceSpecifier ) +{ + return createInstanceWithContext( + rServiceSpecifier, m_xContext ); +} + +// XMultipleServiceFactory +Reference<XInterface > OServiceManager::createInstanceWithArguments( + const OUString& rServiceSpecifier, + const Sequence<Any >& rArguments ) +{ + return createInstanceWithArgumentsAndContext( + rServiceSpecifier, rArguments, m_xContext ); +} + +// XInitialization +void OServiceManager::initialize( Sequence< Any > const & ) +{ + check_undisposed(); + OSL_FAIL( "not impl!" ); +} + +// XServiceInfo +OUString OServiceManager::getImplementationName() +{ + return "com.sun.star.comp.stoc.OServiceManager"; +} + +// XServiceInfo +sal_Bool OServiceManager::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > OServiceManager::getSupportedServiceNames() +{ + return { "com.sun.star.lang.MultiServiceFactory", "com.sun.star.lang.ServiceManager" }; +} + + +Sequence< Reference< XInterface > > OServiceManager::queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & ) +{ + Sequence< Reference< XInterface > > ret; + + MutexGuard aGuard( m_aMutex ); + ::std::pair< + HashMultimap_OWString_Interface::iterator, + HashMultimap_OWString_Interface::iterator> p( + m_ServiceMap.equal_range( aServiceName ) ); + + if (p.first == p.second) // no factories + { + // no service found, look for an implementation + HashMap_OWString_Interface::iterator aIt = m_ImplementationNameMap.find( aServiceName ); + if( aIt != m_ImplementationNameMap.end() ) + { + Reference< XInterface > const & x = aIt->second; + // an implementation found + ret = Sequence< Reference< XInterface > >( &x, 1 ); + } + } + else + { + ::std::vector< Reference< XInterface > > vec; + vec.reserve( 4 ); + while (p.first != p.second) + { + vec.push_back( p.first->second ); + ++p.first; + } + ret = Sequence< Reference< XInterface > >( vec.data(), vec.size() ); + } + + return ret; +} + +// XContentEnumerationAccess +Reference<XEnumeration > OServiceManager::createContentEnumeration( + const OUString& aServiceName ) +{ + check_undisposed(); + Sequence< Reference< XInterface > > factories( + OServiceManager::queryServiceFactories( aServiceName, m_xContext ) ); + if (factories.hasElements()) + return new ServiceEnumeration_Impl( factories ); + else + return Reference< XEnumeration >(); +} + +// XEnumeration +Reference<XEnumeration > OServiceManager::createEnumeration() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + return new ImplementationEnumeration_Impl( m_ImplementationMap ); +} + +// XElementAccess +Type OServiceManager::getElementType() +{ + check_undisposed(); + return cppu::UnoType<XInterface>::get(); +} + +// XElementAccess +sal_Bool OServiceManager::hasElements() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + return !m_ImplementationMap.empty(); +} + +// XSet +sal_Bool OServiceManager::has( const Any & Element ) +{ + check_undisposed(); + if( Element.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference<XInterface > xEle( Element, UNO_QUERY_THROW ); + MutexGuard aGuard( m_aMutex ); + return m_ImplementationMap.find( xEle ) != + m_ImplementationMap.end(); + } + else if (auto implName = o3tl::tryAccess<OUString>(Element)) + { + MutexGuard aGuard( m_aMutex ); + return m_ImplementationNameMap.find( *implName ) != + m_ImplementationNameMap.end(); + } + return false; +} + +// XSet +void OServiceManager::insert( const Any & Element ) +{ + check_undisposed(); + if( Element.getValueTypeClass() != TypeClass_INTERFACE ) + { + throw IllegalArgumentException( + "exception interface, got " + Element.getValueType().getTypeName(), + Reference< XInterface >(), 0 ); + } + Reference<XInterface > xEle( Element, UNO_QUERY_THROW ); + + { + MutexGuard aGuard( m_aMutex ); + HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle ); + if( aIt != m_ImplementationMap.end() ) + { + throw ElementExistException( "element already exists!" ); + } + + // put into the implementation hashmap + m_ImplementationMap.insert( xEle ); + + // put into the implementation name hashmap + Reference<XServiceInfo > xInfo( Reference<XServiceInfo >::query( xEle ) ); + if( xInfo.is() ) + { + OUString aImplName = xInfo->getImplementationName(); + if( !aImplName.isEmpty() ) + m_ImplementationNameMap[ aImplName ] = xEle; + + //put into the service map + const Sequence< OUString > aServiceNames = xInfo->getSupportedServiceNames(); + for( const OUString& rServiceName : aServiceNames ) + { + m_ServiceMap.emplace( + rServiceName, *o3tl::doAccess<Reference<XInterface>>(Element) ); + } + } + } + // add the disposing listener to the factory + Reference<XComponent > xComp( Reference<XComponent >::query( xEle ) ); + if( xComp.is() ) + xComp->addEventListener( getFactoryListener() ); +} + +// helper function +bool OServiceManager::haveFactoryWithThisImplementation(const OUString& aImplName) +{ + return ( m_ImplementationNameMap.find(aImplName) != m_ImplementationNameMap.end()); +} + +// XSet +void OServiceManager::remove( const Any & Element ) +{ + if (is_disposed()) + return; + + Reference<XInterface > xEle; + if (Element.getValueTypeClass() == TypeClass_INTERFACE) + { + xEle.set( Element, UNO_QUERY_THROW ); + } + else if (auto implName = o3tl::tryAccess<OUString>(Element)) + { + MutexGuard aGuard( m_aMutex ); + HashMap_OWString_Interface::const_iterator const iFind( + m_ImplementationNameMap.find( *implName ) ); + if (iFind == m_ImplementationNameMap.end()) + { + throw NoSuchElementException( + "element is not in: " + *implName, + getXWeak() ); + } + xEle = iFind->second; + } + else + { + throw IllegalArgumentException( + "expected interface or string, got " + Element.getValueType().getTypeName(), + Reference< XInterface >(), 0 ); + } + + // remove the disposing listener from the factory + Reference<XComponent > xComp( Reference<XComponent >::query( xEle ) ); + if( xComp.is() ) + xComp->removeEventListener( getFactoryListener() ); + + MutexGuard aGuard( m_aMutex ); + HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle ); + if( aIt == m_ImplementationMap.end() ) + { + throw NoSuchElementException( + "element not found", + getXWeak() ); + } + //First remove all factories which have been loaded by ORegistryServiceManager. + m_SetLoadedFactories.erase( *aIt); + //Remove from the implementation map. It contains all factories of m_SetLoadedFactories + //which have been added directly through XSet, that is not via ORegistryServiceManager + m_ImplementationMap.erase( aIt ); + + // remove from the implementation name hashmap + Reference<XServiceInfo > xInfo( Reference<XServiceInfo >::query( xEle ) ); + if( xInfo.is() ) + { + OUString aImplName = xInfo->getImplementationName(); + if( !aImplName.isEmpty() ) + m_ImplementationNameMap.erase( aImplName ); + } + + //remove from the service map + Reference<XServiceInfo > xSF( Reference<XServiceInfo >::query( xEle ) ); + if( !xSF.is() ) + return; + + const Sequence< OUString > aServiceNames = xSF->getSupportedServiceNames(); + for( const OUString& rServiceName : aServiceNames ) + { + std::pair<HashMultimap_OWString_Interface::iterator, HashMultimap_OWString_Interface::iterator> p = + m_ServiceMap.equal_range( rServiceName ); + + while( p.first != p.second ) + { + if( xEle == (*p.first).second ) + { + m_ServiceMap.erase( p.first ); + break; + } + ++p.first; + } + } +} + +/***************************************************************************** + class ORegistryServiceManager +*****************************************************************************/ +class ORegistryServiceManager : public OServiceManager +{ +public: + explicit ORegistryServiceManager( Reference< XComponentContext > const & xContext ); + + // XInitialization + void SAL_CALL initialize(const Sequence< Any >& Arguments) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.stoc.ORegistryServiceManager"; } + + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XMultiServiceFactory + Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + // XContentEnumerationAccess + //Sequence< OUString > getAvailableServiceNames() throw( (Exception) ); + Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override; + + // XComponent + void SAL_CALL dispose() override; + + // OServiceManager + Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + Any SAL_CALL getPropertyValue(const OUString& PropertyName) override; + +protected: + //OServiceManager + Sequence< Reference< XInterface > > queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & xContext ) override; +private: + Reference<XRegistryKey > getRootKey(); + Reference<XInterface > loadWithImplementationName( + const OUString & rImplName, Reference< XComponentContext > const & xContext ); + Sequence<OUString> getFromServiceName(std::u16string_view serviceName) const; + Reference<XInterface > loadWithServiceName( + std::u16string_view rImplName, Reference< XComponentContext > const & xContext ); + void fillAllNamesFromRegistry( HashSet_OWString & ); + + bool m_searchedRegistry; + Reference<XSimpleRegistry > m_xRegistry; // readonly property Registry + Reference<XRegistryKey > m_xRootKey; + +#if OSL_DEBUG_LEVEL > 0 + bool m_init; +#endif +}; + +/** + * Create a ServiceManager + */ +ORegistryServiceManager::ORegistryServiceManager( Reference< XComponentContext > const & xContext ) + : OServiceManager( xContext ) + , m_searchedRegistry(false) +#if OSL_DEBUG_LEVEL > 0 + , m_init( false ) +#endif +{ +} + +// XComponent +void ORegistryServiceManager::dispose() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + OServiceManager::dispose(); + // dispose + MutexGuard aGuard( m_aMutex ); + // erase all members + m_xRegistry.clear(); + m_xRootKey.clear(); +} + +/** + * Return the root key of the registry. The Default registry service is ordered + * if no registry is set. + */ +//Reference<XServiceProvider > create_DefaultRegistry_ServiceProvider(); + +Reference<XRegistryKey > ORegistryServiceManager::getRootKey() +{ + if( !m_xRootKey.is() ) + { + MutexGuard aGuard( m_aMutex ); + // DefaultRegistry suchen !!!! + if( !m_xRegistry.is() && !m_searchedRegistry ) + { + // NB. we only search this once + m_searchedRegistry = true; + + m_xRegistry.set( + createInstanceWithContext( + "com.sun.star.registry.DefaultRegistry", + m_xContext ), + UNO_QUERY ); + } + if( m_xRegistry.is() && !m_xRootKey.is() ) + m_xRootKey = m_xRegistry->getRootKey(); + } + + return m_xRootKey; +} + +/** + * Create a service provider from the registry with an implementation name + */ +Reference<XInterface > ORegistryServiceManager::loadWithImplementationName( + const OUString& name, Reference< XComponentContext > const & xContext ) +{ + Reference<XInterface > ret; + + Reference<XRegistryKey > xRootKey = getRootKey(); + if( !xRootKey.is() ) + return ret; + + try + { + OUString implementationName = "/IMPLEMENTATIONS/" + name; + Reference<XRegistryKey > xImpKey = m_xRootKey->openKey(implementationName); + + if( xImpKey.is() ) + { + Reference< lang::XMultiServiceFactory > xMgr; + if (xContext.is()) + xMgr.set( xContext->getServiceManager(), UNO_QUERY_THROW ); + else + xMgr.set( this ); + ret = createSingleRegistryFactory( xMgr, name, xImpKey ); + insert( Any( ret ) ); + // Remember this factory as loaded in contrast to inserted ( XSet::insert) + // factories. Those loaded factories in this set are candidates for being + // released on an unloading notification. + m_SetLoadedFactories.insert( ret); + } + } + catch (InvalidRegistryException &) + { + } + + return ret; +} + +/** + * Return all implementation out of the registry. + */ +Sequence<OUString> ORegistryServiceManager::getFromServiceName( + std::u16string_view serviceName ) const +{ + OUString buf = OUString::Concat("/SERVICES/") + serviceName; + return retrieveAsciiValueList( m_xRegistry, buf ); +} + +/** + * Create a service provider from the registry + */ +Reference<XInterface > ORegistryServiceManager::loadWithServiceName( + std::u16string_view serviceName, Reference< XComponentContext > const & xContext ) +{ + const Sequence<OUString> implEntries = getFromServiceName( serviceName ); + for (const auto& rEntry : implEntries) + { + Reference< XInterface > x( loadWithImplementationName( rEntry, xContext ) ); + if (x.is()) + return x; + } + + return Reference<XInterface >(); +} + +/** + * Return a sequence of all service names from the registry. + */ +void ORegistryServiceManager::fillAllNamesFromRegistry( HashSet_OWString & rSet ) +{ + Reference<XRegistryKey > xRootKey = getRootKey(); + if( !xRootKey.is() ) + return; + + try + { + Reference<XRegistryKey > xServicesKey = xRootKey->openKey( "SERVICES" ); + // root + /Services + / + if( xServicesKey.is() ) + { + sal_Int32 nPrefix = xServicesKey->getKeyName().getLength() +1; + const Sequence<Reference<XRegistryKey > > aKeys = xServicesKey->openKeys(); + std::transform(aKeys.begin(), aKeys.end(), std::inserter(rSet, rSet.end()), + [nPrefix](const Reference<XRegistryKey>& rKey) -> OUString { + return rKey->getKeyName().copy( nPrefix ); }); + } + } + catch (InvalidRegistryException &) + { + } +} + +// XInitialization +void ORegistryServiceManager::initialize(const Sequence< Any >& Arguments) +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + if (Arguments.hasElements()) + { + m_xRootKey.clear(); + Arguments[ 0 ] >>= m_xRegistry; + } +#if OSL_DEBUG_LEVEL > 0 + // to find all bootstrapping processes to be fixed... + OSL_ENSURE( !m_init, "### second init of service manager instance!" ); + m_init = true; +#endif +} + +// XMultiServiceFactory, XContentEnumeration +Sequence< OUString > ORegistryServiceManager::getAvailableServiceNames() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + // all names + HashSet_OWString aNameSet; + + // all names from the registry + fillAllNamesFromRegistry( aNameSet ); + + return OServiceManager::getUniqueAvailableServiceNames( aNameSet ); +} + +// XServiceInfo +Sequence< OUString > ORegistryServiceManager::getSupportedServiceNames() +{ + return { "com.sun.star.lang.MultiServiceFactory", "com.sun.star.lang.RegistryServiceManager" }; +} + + +// OServiceManager +Sequence< Reference< XInterface > > ORegistryServiceManager::queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & xContext ) +{ + Sequence< Reference< XInterface > > ret( + OServiceManager::queryServiceFactories( aServiceName, xContext ) ); + if (ret.hasElements()) + { + return ret; + } + else + { + MutexGuard aGuard( m_aMutex ); + Reference< XInterface > x( loadWithServiceName( aServiceName, xContext ) ); + if (! x.is()) + x = loadWithImplementationName( aServiceName, xContext ); + return Sequence< Reference< XInterface > >( &x, 1 ); + } +} + +// XContentEnumerationAccess +Reference<XEnumeration > ORegistryServiceManager::createContentEnumeration( + const OUString& aServiceName ) +{ + check_undisposed(); + MutexGuard aGuard(m_aMutex); + // get all implementation names registered under this service name from the registry + const Sequence<OUString> aImpls = getFromServiceName( aServiceName ); + // load and insert all factories specified by the registry + for( const OUString& aImplName : aImpls ) + { + if ( !haveFactoryWithThisImplementation(aImplName) ) + { + loadWithImplementationName( aImplName, m_xContext ); + } + } + // call the superclass to enumerate all contents + return OServiceManager::createContentEnumeration( aServiceName ); +} + +// OServiceManager +Reference<XPropertySetInfo > ORegistryServiceManager::getPropertySetInfo() +{ + check_undisposed(); + if (! m_xPropertyInfo.is()) + { + Sequence< beans::Property > seq{ + beans::Property("DefaultContext", -1, cppu::UnoType<decltype(m_xContext)>::get(), 0), + beans::Property("Registry", -1, cppu::UnoType<decltype(m_xRegistry)>::get(), + beans::PropertyAttribute::READONLY) + }; + Reference< beans::XPropertySetInfo > xInfo( new PropertySetInfo_Impl( seq ) ); + + MutexGuard aGuard( m_aMutex ); + if (! m_xPropertyInfo.is()) + { + m_xPropertyInfo = xInfo; + } + } + return m_xPropertyInfo; +} + +Any ORegistryServiceManager::getPropertyValue(const OUString& PropertyName) +{ + check_undisposed(); + if ( PropertyName == "Registry" ) + { + MutexGuard aGuard( m_aMutex ); + if( m_xRegistry.is() ) + return Any( m_xRegistry ); + else + return Any(); + } + return OServiceManager::getPropertyValue( PropertyName ); +} + +} // namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_OServiceManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OServiceManager(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_ORegistryServiceManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ORegistryServiceManager(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_OServiceManagerWrapper_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OServiceManagerWrapper(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/simpleregistry/simpleregistry.cxx b/stoc/source/simpleregistry/simpleregistry.cxx new file mode 100644 index 0000000000..f793aa56db --- /dev/null +++ b/stoc/source/simpleregistry/simpleregistry.cxx @@ -0,0 +1,932 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cstdlib> +#include <mutex> +#include <optional> +#include <utility> +#include <vector> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <com/sun/star/registry/InvalidValueException.hpp> +#include <com/sun/star/registry/RegistryKeyType.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <registry/registry.hxx> +#include <registry/regtype.h> +#include <rtl/ref.hxx> +#include <rtl/string.h> +#include <rtl/string.hxx> +#include <rtl/textcvt.h> +#include <rtl/textenc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace { + +class SimpleRegistry: + public cppu::WeakImplHelper< + css::registry::XSimpleRegistry, css::lang::XServiceInfo > +{ +public: + SimpleRegistry(): registry_(Registry()) {} + + ~SimpleRegistry() { + std::scoped_lock guard(mutex_); + registry_.reset(); + } + + std::mutex mutex_; + +private: + virtual OUString SAL_CALL getURL() override; + + virtual void SAL_CALL open( + OUString const & rURL, sal_Bool bReadOnly, sal_Bool bCreate) override; + + virtual sal_Bool SAL_CALL isValid() override; + + virtual void SAL_CALL close() override; + + virtual void SAL_CALL destroy() override; + + virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL + getRootKey() override; + + virtual sal_Bool SAL_CALL isReadOnly() override; + + virtual void SAL_CALL mergeKey( + OUString const & aKeyName, OUString const & aUrl) override; + + virtual OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.stoc.SimpleRegistry"; } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return cppu::supportsService(this, ServiceName); } + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override + { + css::uno::Sequence< OUString > names { "com.sun.star.registry.SimpleRegistry" }; + return names; + } + + std::optional<Registry> registry_; +}; + +class Key: public cppu::WeakImplHelper< css::registry::XRegistryKey > { +public: + Key( + rtl::Reference< SimpleRegistry > registry, + RegistryKey const & key): + registry_(std::move(registry)), key_(key) {} + + ~Key() { + std::scoped_lock guard(registry_->mutex_); + key_.reset(); + } + +private: + virtual OUString SAL_CALL getKeyName() override; + + virtual sal_Bool SAL_CALL isReadOnly() override; + + virtual sal_Bool SAL_CALL isValid() override; + + virtual css::registry::RegistryKeyType SAL_CALL getKeyType( + OUString const & rKeyName) override; + + virtual css::registry::RegistryValueType SAL_CALL getValueType() override; + + virtual sal_Int32 SAL_CALL getLongValue() override; + + virtual void SAL_CALL setLongValue(sal_Int32 value) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getLongListValue() override; + + virtual void SAL_CALL setLongListValue( + css::uno::Sequence< sal_Int32 > const & seqValue) override; + + virtual OUString SAL_CALL getAsciiValue() override; + + virtual void SAL_CALL setAsciiValue(OUString const & value) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getAsciiListValue() override; + + virtual void SAL_CALL setAsciiListValue( + css::uno::Sequence< OUString > const & seqValue) override; + + virtual OUString SAL_CALL getStringValue() override; + + virtual void SAL_CALL setStringValue(OUString const & value) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getStringListValue() override; + + virtual void SAL_CALL setStringListValue( + css::uno::Sequence< OUString > const & seqValue) override; + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBinaryValue() override; + + virtual void SAL_CALL setBinaryValue( + css::uno::Sequence< sal_Int8 > const & value) override; + + virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL openKey( + OUString const & aKeyName) override; + + virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL + createKey(OUString const & aKeyName) override; + + virtual void SAL_CALL closeKey() override; + + virtual void SAL_CALL deleteKey(OUString const & rKeyName) override; + + virtual + css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > > + SAL_CALL openKeys() override; + + virtual css::uno::Sequence< OUString > SAL_CALL getKeyNames() override; + + virtual sal_Bool SAL_CALL createLink( + OUString const & aLinkName, OUString const & aLinkTarget) override; + + virtual void SAL_CALL deleteLink(OUString const & rLinkName) override; + + virtual OUString SAL_CALL getLinkTarget(OUString const & rLinkName) override; + + virtual OUString SAL_CALL getResolvedName(OUString const & aKeyName) override; + + rtl::Reference< SimpleRegistry > registry_; + std::optional<RegistryKey> key_; +}; + +OUString Key::getKeyName() { + std::scoped_lock guard(registry_->mutex_); + return key_->getName(); +} + +sal_Bool Key::isReadOnly() +{ + std::scoped_lock guard(registry_->mutex_); + return key_->isReadOnly(); +} + +sal_Bool Key::isValid() { + std::scoped_lock guard(registry_->mutex_); + return key_->isValid(); +} + +css::registry::RegistryKeyType Key::getKeyType(OUString const & ) +{ + return css::registry::RegistryKeyType_KEY; +} + +css::registry::RegistryValueType Key::getValueType() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_->getValueInfo(OUString(), &type, &size); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::INVALID_VALUE: + type = RegValueType::NOT_DEFINED; + break; + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getValueType:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + switch (type) { + default: + std::abort(); // this cannot happen + // pseudo-fall-through to avoid warnings on MSC + case RegValueType::NOT_DEFINED: + return css::registry::RegistryValueType_NOT_DEFINED; + case RegValueType::LONG: + return css::registry::RegistryValueType_LONG; + case RegValueType::STRING: + return css::registry::RegistryValueType_ASCII; + case RegValueType::UNICODE: + return css::registry::RegistryValueType_STRING; + case RegValueType::BINARY: + return css::registry::RegistryValueType_BINARY; + case RegValueType::LONGLIST: + return css::registry::RegistryValueType_LONGLIST; + case RegValueType::STRINGLIST: + return css::registry::RegistryValueType_ASCIILIST; + case RegValueType::UNICODELIST: + return css::registry::RegistryValueType_STRINGLIST; + } +} + +sal_Int32 Key::getLongValue() +{ + std::scoped_lock guard(registry_->mutex_); + sal_Int32 value; + RegError err = key_->getValue(OUString(), &value); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getLongValue:" + " underlying RegistryKey::getValue() = RegError::INVALID_VALUE", + getXWeak()); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getLongValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + return value; +} + +void Key::setLongValue(sal_Int32 value) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_->setValue( + OUString(), RegValueType::LONG, &value, sizeof (sal_Int32)); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setLongValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Sequence< sal_Int32 > Key::getLongListValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryValueList< sal_Int32 > list; + RegError err = key_->getLongListValue(OUString(), list); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::VALUE_NOT_EXISTS: + return css::uno::Sequence< sal_Int32 >(); + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getLongListValue:" + " underlying RegistryKey::getLongListValue() =" + " RegError::INVALID_VALUE", + getXWeak()); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getLongListValue:" + " underlying RegistryKey::getLongListValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getLongListValue:" + " underlying RegistryKey::getLongListValue() too large", + getXWeak()); + } + css::uno::Sequence< sal_Int32 > value(static_cast< sal_Int32 >(n)); + auto aValueRange = asNonConstRange(value); + for (sal_uInt32 i = 0; i < n; ++i) { + aValueRange[static_cast< sal_Int32 >(i)] = list.getElement(i); + } + return value; +} + +void Key::setLongListValue(css::uno::Sequence< sal_Int32 > const & seqValue) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_->setLongListValue( + OUString(), seqValue.getConstArray(), static_cast< sal_uInt32 >(seqValue.getLength())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setLongListValue:" + " underlying RegistryKey::setLongListValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +OUString Key::getAsciiValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_->getValueInfo(OUString(), &type, &size); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + if (type != RegValueType::STRING) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey type = " + OUString::number(static_cast<int>(type)), + getXWeak()); + } + // size contains terminating null (error in underlying registry.cxx): + if (size == 0) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey size 0 cannot happen due to" + " design error", + getXWeak()); + } + if (size > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey size too large", + getXWeak()); + } + std::vector< char > list(size); + err = key_->getValue(OUString(), list.data()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + if (list[size - 1] != '\0') { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey value must be null-terminated due" + " to design error", + getXWeak()); + } + OUString value; + if (!rtl_convertStringToUString( + &value.pData, list.data(), + static_cast< sal_Int32 >(size - 1), RTL_TEXTENCODING_UTF8, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey not UTF-8", + getXWeak()); + } + return value; +} + +void Key::setAsciiValue(OUString const & value) +{ + std::scoped_lock guard(registry_->mutex_); + OString utf8; + if (!value.convertToString( + &utf8, RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + throw css::uno::RuntimeException( + "com.sun.star.registry.SimpleRegistry key setAsciiValue:" + " value not UTF-16", + getXWeak()); + } + RegError err = key_->setValue( + OUString(), RegValueType::STRING, + const_cast< char * >(utf8.getStr()), utf8.getLength() + 1); + // +1 for terminating null (error in underlying registry.cxx) + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setAsciiValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Sequence< OUString > Key::getAsciiListValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryValueList< char * > list; + RegError err = key_->getStringListValue(OUString(), list); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::VALUE_NOT_EXISTS: + return css::uno::Sequence< OUString >(); + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying" + " RegistryKey::getStringListValue() = RegError::INVALID_VALUE", + getXWeak()); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying" + " RegistryKey::getStringListValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying" + " RegistryKey::getStringListValue() too large", + getXWeak()); + } + css::uno::Sequence< OUString > value(static_cast< sal_Int32 >(n)); + auto aValueRange = asNonConstRange(value); + for (sal_uInt32 i = 0; i < n; ++i) { + char * el = list.getElement(i); + sal_Int32 size = rtl_str_getLength(el); + if (!rtl_convertStringToUString( + &aValueRange[static_cast< sal_Int32 >(i)].pData, el, size, + RTL_TEXTENCODING_UTF8, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying RegistryKey not" + " UTF-8", + getXWeak()); + } + } + return value; +} + +void Key::setAsciiListValue( + css::uno::Sequence< OUString > const & seqValue) +{ + std::scoped_lock guard(registry_->mutex_); + std::vector< OString > list; + for (const auto& rValue : seqValue) { + OString utf8; + if (!rValue.convertToString( + &utf8, RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + throw css::uno::RuntimeException( + "com.sun.star.registry.SimpleRegistry key" + " setAsciiListValue: value not UTF-16", + getXWeak()); + } + list.push_back(utf8); + } + std::vector< char * > list2; + for (const auto& rItem : list) + { + list2.push_back(const_cast< char * >(rItem.getStr())); + } + RegError err = key_->setStringListValue( + OUString(), list2.data(), static_cast< sal_uInt32 >(list2.size())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " setAsciiListValue: underlying" + " RegistryKey::setStringListValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +OUString Key::getStringValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_->getValueInfo(OUString(), &type, &size); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + if (type != RegValueType::UNICODE) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey type = " + OUString::number(static_cast<int>(type)), + getXWeak()); + } + // size contains terminating null and is *2 (error in underlying + // registry.cxx): + if (size == 0 || (size & 1) == 1) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey size 0 or odd cannot happen due to" + " design error", + getXWeak()); + } + if (size > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey size too large", + getXWeak()); + } + std::vector< sal_Unicode > list(size); + err = key_->getValue(OUString(), list.data()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + if (list[size/2 - 1] != 0) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey value must be null-terminated due" + " to design error", + getXWeak()); + } + return OUString(list.data(), static_cast< sal_Int32 >(size/2 - 1)); +} + +void Key::setStringValue(OUString const & value) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_->setValue( + OUString(), RegValueType::UNICODE, + const_cast< sal_Unicode * >(value.getStr()), + (value.getLength() + 1) * sizeof (sal_Unicode)); + // +1 for terminating null (error in underlying registry.cxx) + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setStringValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Sequence< OUString > Key::getStringListValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryValueList< sal_Unicode * > list; + RegError err = key_->getUnicodeListValue(OUString(), list); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::VALUE_NOT_EXISTS: + return css::uno::Sequence< OUString >(); + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getStringListValue: underlying" + " RegistryKey::getUnicodeListValue() = RegError::INVALID_VALUE", + getXWeak()); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " getStringListValue: underlying" + " RegistryKey::getUnicodeListValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getStringListValue: underlying" + " RegistryKey::getUnicodeListValue() too large", + getXWeak()); + } + css::uno::Sequence< OUString > value(static_cast< sal_Int32 >(n)); + auto aValueRange = asNonConstRange(value); + for (sal_uInt32 i = 0; i < n; ++i) { + aValueRange[static_cast< sal_Int32 >(i)] = list.getElement(i); + } + return value; +} + +void Key::setStringListValue( + css::uno::Sequence< OUString > const & seqValue) +{ + std::scoped_lock guard(registry_->mutex_); + std::vector< sal_Unicode * > list; + list.reserve(seqValue.getLength()); + std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(list), + [](const OUString& rValue) -> sal_Unicode* { return const_cast<sal_Unicode*>(rValue.getStr()); }); + RegError err = key_->setUnicodeListValue( + OUString(), list.data(), static_cast< sal_uInt32 >(list.size())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " setStringListValue: underlying" + " RegistryKey::setUnicodeListValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Sequence< sal_Int8 > Key::getBinaryValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_->getValueInfo(OUString(), &type, &size); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + if (type != RegValueType::BINARY) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey type = " + OUString::number(static_cast<int>(type)), + getXWeak()); + } + if (size > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey size too large", + getXWeak()); + } + css::uno::Sequence< sal_Int8 > value(static_cast< sal_Int32 >(size)); + err = key_->getValue(OUString(), value.getArray()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + return value; +} + +void Key::setBinaryValue(css::uno::Sequence< sal_Int8 > const & value) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_->setValue( + OUString(), RegValueType::BINARY, + const_cast< sal_Int8 * >(value.getConstArray()), + static_cast< sal_uInt32 >(value.getLength())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setBinaryValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Reference< css::registry::XRegistryKey > Key::openKey( + OUString const & aKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKey key; + RegError err = key_->openKey(aKeyName, key); + switch (err) { + case RegError::NO_ERROR: + return new Key(registry_, key); + case RegError::KEY_NOT_EXISTS: + return css::uno::Reference< css::registry::XRegistryKey >(); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key openKey:" + " underlying RegistryKey::openKey() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Reference< css::registry::XRegistryKey > Key::createKey( + OUString const & aKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKey key; + RegError err = key_->createKey(aKeyName, key); + switch (err) { + case RegError::NO_ERROR: + return new Key(registry_, key); + case RegError::INVALID_KEYNAME: + return css::uno::Reference< css::registry::XRegistryKey >(); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key createKey:" + " underlying RegistryKey::createKey() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +void Key::closeKey() +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_->closeKey(); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key closeKey:" + " underlying RegistryKey::closeKey() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +void Key::deleteKey(OUString const & rKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_->deleteKey(rKeyName); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key deleteKey:" + " underlying RegistryKey::deleteKey() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > > +Key::openKeys() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKeyArray list; + RegError err = key_->openSubKeys(OUString(), list); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key openKeys:" + " underlying RegistryKey::openSubKeys() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getKeyNames:" + " underlying RegistryKey::getKeyNames() too large", + getXWeak()); + } + css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > > + keys(static_cast< sal_Int32 >(n)); + auto aKeysRange = asNonConstRange(keys); + for (sal_uInt32 i = 0; i < n; ++i) { + aKeysRange[static_cast< sal_Int32 >(i)] = new Key( + registry_, list.getElement(i)); + } + return keys; +} + +css::uno::Sequence< OUString > Key::getKeyNames() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKeyNames list; + RegError err = key_->getKeyNames(OUString(), list); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getKeyNames:" + " underlying RegistryKey::getKeyNames() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getKeyNames:" + " underlying RegistryKey::getKeyNames() too large", + getXWeak()); + } + css::uno::Sequence< OUString > names(static_cast< sal_Int32 >(n)); + auto aNamesRange = asNonConstRange(names); + for (sal_uInt32 i = 0; i < n; ++i) { + aNamesRange[static_cast< sal_Int32 >(i)] = list.getElement(i); + } + return names; +} + +sal_Bool Key::createLink( + OUString const & /*aLinkName*/, OUString const & /*aLinkTarget*/) +{ + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key createLink: links are no longer supported", + getXWeak()); +} + +void Key::deleteLink(OUString const & /*rLinkName*/) +{ + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key deleteLink: links are no longer supported", + getXWeak()); +} + +OUString Key::getLinkTarget(OUString const & /*rLinkName*/) +{ + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getLinkTarget: links are no longer supported", + getXWeak()); +} + +OUString Key::getResolvedName(OUString const & aKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + OUString resolved; + RegError err = key_->getResolvedKeyName(aKeyName, resolved); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getResolvedName:" + " underlying RegistryKey::getResolvedName() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + return resolved; +} + +OUString SimpleRegistry::getURL() { + std::scoped_lock guard(mutex_); + return registry_->getName(); +} + +void SimpleRegistry::open( + OUString const & rURL, sal_Bool bReadOnly, sal_Bool bCreate) +{ + std::scoped_lock guard(mutex_); + RegError err = (rURL.isEmpty() && bCreate) + ? RegError::REGISTRY_NOT_EXISTS + : registry_->open(rURL, bReadOnly ? RegAccessMode::READONLY : RegAccessMode::READWRITE); + if (err == RegError::REGISTRY_NOT_EXISTS && bCreate) { + err = registry_->create(rURL); + } + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.open(" + rURL + + "): underlying Registry::open/create() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +sal_Bool SimpleRegistry::isValid() { + std::scoped_lock guard(mutex_); + return registry_->isValid(); +} + +void SimpleRegistry::close() +{ + std::scoped_lock guard(mutex_); + RegError err = registry_->close(); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.close:" + " underlying Registry::close() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +void SimpleRegistry::destroy() +{ + std::scoped_lock guard(mutex_); + RegError err = registry_->destroy(OUString()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.destroy:" + " underlying Registry::destroy() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } +} + +css::uno::Reference< css::registry::XRegistryKey > SimpleRegistry::getRootKey() +{ + std::scoped_lock guard(mutex_); + RegistryKey root; + RegError err = registry_->openRootKey(root); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.getRootKey:" + " underlying Registry::getRootKey() = " + OUString::number(static_cast<int>(err)), + getXWeak()); + } + return new Key(this, root); +} + +sal_Bool SimpleRegistry::isReadOnly() +{ + std::scoped_lock guard(mutex_); + return registry_->isReadOnly(); +} + +void SimpleRegistry::mergeKey( + OUString const &, OUString const &) +{ + throw css::uno::RuntimeException("css.registry.SimpleRegistry::mergeKey: not implemented"); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_SimpleRegistry_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SimpleRegistry); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/typeconv/convert.cxx b/stoc/source/typeconv/convert.cxx new file mode 100644 index 0000000000..c3cd1b9997 --- /dev/null +++ b/stoc/source/typeconv/convert.cxx @@ -0,0 +1,872 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <o3tl/underlyingenumvalue.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <typelib/typedescription.hxx> +#include <uno/data.h> + +#ifdef _WIN32 +#include <cmath> +#else +#include <math.h> +#endif +#include <float.h> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/FailReason.hpp> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace css::uno; +using namespace css::lang; +using namespace css::script; +using namespace cppu; +using namespace osl; + +namespace stoc_tcv +{ + +static double round( double aVal ) +{ + bool bPos = (aVal >= 0.0); + aVal = ::fabs( aVal ); + double aUpper = ::ceil( aVal ); + + aVal = ((aUpper-aVal) <= 0.5) ? aUpper : (aUpper - 1.0); + return (bPos ? aVal : -aVal); +} + + +static bool getNumericValue( double & rfVal, std::u16string_view rStr ) +{ + double fRet = o3tl::toDouble(rStr); + if (fRet == 0.0) + { + size_t nLen = rStr.size(); + if (!nLen || (nLen == 1 && rStr[0] == '0')) // common case + { + rfVal = 0.0; + return true; + } + + std::u16string_view trim( o3tl::trim(rStr) ); + + // try hex + size_t nX = trim.find( 'x' ); + if (nX == std::u16string_view::npos) + nX = trim.find( 'X' ); + + if (nX > 0 && nX != std::u16string_view::npos && trim[nX-1] == '0') // 0x + { + bool bNeg = false; + switch (nX) + { + case 2: // (+|-)0x... + if (trim[0] == '-') + bNeg = true; + else if (trim[0] != '+') + return false; + break; + case 1: // 0x... + break; + default: + return false; + } + + OUString aHexRest( trim.substr( nX+1 ) ); + sal_uInt64 nRet = aHexRest.toUInt64( 16 ); + + if (nRet == 0) + { + for ( sal_Int32 nPos = aHexRest.getLength(); nPos--; ) + { + if (aHexRest[nPos] != '0') + return false; + } + } + + rfVal = (bNeg ? -static_cast<double>(nRet) : static_cast<double>(nRet)); + return true; + } + + nLen = trim.size(); + size_t nPos = 0; + + // skip +/- + if (nLen && (trim[0] == '-' || trim[0] == '+')) + ++nPos; + + while (nPos < nLen) // skip leading zeros + { + if (trim[nPos] != '0') + { + if (trim[nPos] != '.') + return false; + ++nPos; + while (nPos < nLen) // skip trailing zeros + { + if (trim[nPos] != '0') + return false; + ++nPos; + } + break; + } + ++nPos; + } + } + rfVal = fRet; + return true; +} + + +static bool getHyperValue( sal_Int64 & rnVal, std::u16string_view rStr ) +{ + size_t nLen = rStr.size(); + if (!nLen || (nLen == 1 && rStr[0] == '0')) // common case + { + rnVal = 0; + return true; + } + + std::u16string_view trim( o3tl::trim(rStr) ); + + // try hex + size_t nX = trim.find( 'x' ); + if (nX == std::u16string_view::npos) + nX = trim.find( 'X' ); + + if (nX != std::u16string_view::npos) + { + if (nX > 0 && trim[nX-1] == '0') // 0x + { + bool bNeg = false; + switch (nX) + { + case 2: // (+|-)0x... + if (trim[0] == '-') + bNeg = true; + else if (trim[0] != '+') + return false; + break; + case 1: // 0x... + break; + default: + return false; + } + + OUString aHexRest( trim.substr( nX+1 ) ); + sal_uInt64 nRet = aHexRest.toUInt64( 16 ); + + if (nRet == 0) + { + for ( sal_Int32 nPos = aHexRest.getLength(); nPos--; ) + { + if (aHexRest[nPos] != '0') + return false; + } + } + + rnVal = (bNeg ? -static_cast<sal_Int64>(nRet) : nRet); + return true; + } + return false; + } + + double fVal; + if (getNumericValue( fVal, rStr ) && + fVal >= double(SAL_MIN_INT64) && + fVal <= double(SAL_MAX_UINT64)) + { + rnVal = static_cast<sal_Int64>(round( fVal )); + return true; + } + return false; +} + +namespace { + +class TypeConverter_Impl : public WeakImplHelper< XTypeConverter, XServiceInfo > +{ + // ...misc helpers... + /// @throws CannotConvertException + static sal_Int64 toHyper( + const Any& rAny, sal_Int64 min, sal_uInt64 max = SAL_MAX_UINT64 ); + /// @throws CannotConvertException + static double toDouble( const Any& rAny, double min = -DBL_MAX, double max = DBL_MAX ); + +public: + TypeConverter_Impl(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XTypeConverter + virtual Any SAL_CALL convertTo( const Any& aFrom, const Type& DestinationType ) override; + virtual Any SAL_CALL convertToSimpleType( const Any& aFrom, TypeClass aDestinationType ) override; +}; + +} + +TypeConverter_Impl::TypeConverter_Impl() {} + +// XServiceInfo +OUString TypeConverter_Impl::getImplementationName() +{ + return "com.sun.star.comp.stoc.TypeConverter"; +} + +// XServiceInfo +sal_Bool TypeConverter_Impl::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > TypeConverter_Impl::getSupportedServiceNames() +{ + return { "com.sun.star.script.Converter" }; +} + + +sal_Int64 TypeConverter_Impl::toHyper( const Any& rAny, sal_Int64 min, sal_uInt64 max ) +{ + sal_Int64 nRet; + TypeClass aDestinationClass = rAny.getValueTypeClass(); + + switch (aDestinationClass) + { + // ENUM + case TypeClass_ENUM: + nRet = *static_cast<sal_Int32 const *>(rAny.getValue()); + break; + // BOOL + case TypeClass_BOOLEAN: + nRet = *o3tl::forceAccess<bool>(rAny) ? 1 : 0; + break; + // CHAR, BYTE + case TypeClass_CHAR: + nRet = *o3tl::forceAccess<sal_Unicode>(rAny); + break; + case TypeClass_BYTE: + nRet = *o3tl::forceAccess<sal_Int8>(rAny); + break; + // SHORT + case TypeClass_SHORT: + nRet = *o3tl::forceAccess<sal_Int16>(rAny); + break; + // UNSIGNED SHORT + case TypeClass_UNSIGNED_SHORT: + nRet = *o3tl::forceAccess<sal_uInt16>(rAny); + break; + // LONG + case TypeClass_LONG: + nRet = *o3tl::forceAccess<sal_Int32>(rAny); + break; + // UNSIGNED LONG + case TypeClass_UNSIGNED_LONG: + nRet = *o3tl::forceAccess<sal_uInt32>(rAny); + break; + // HYPER + case TypeClass_HYPER: + nRet = *o3tl::forceAccess<sal_Int64>(rAny); + break; + // UNSIGNED HYPER + case TypeClass_UNSIGNED_HYPER: + { + auto const n = *o3tl::forceAccess<sal_uInt64>(rAny); + if ((min < 0 || n >= o3tl::make_unsigned(min)) && // lower bound + n <= max) // upper bound + { + return static_cast<sal_Int64>(n); + } + throw CannotConvertException( + "UNSIGNED HYPER out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + + // FLOAT, DOUBLE + case TypeClass_FLOAT: + { + double fVal = round( *o3tl::forceAccess<float>(rAny) ); + if (fVal >= min && fVal <= max) + { + nRet = (fVal >= 0.0 ? static_cast<sal_Int64>(static_cast<sal_uInt64>(fVal)) : static_cast<sal_Int64>(fVal)); + return nRet; + } + throw CannotConvertException( + "FLOAT out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + case TypeClass_DOUBLE: + { + double fVal = round( *o3tl::forceAccess<double>(rAny) ); + if (fVal >= min && fVal <= max) + { + nRet = (fVal >= 0.0 ? static_cast<sal_Int64>(static_cast<sal_uInt64>(fVal)) : static_cast<sal_Int64>(fVal)); + return nRet; + } + throw CannotConvertException( + "DOUBLE out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + + // STRING + case TypeClass_STRING: + { + sal_Int64 nVal = SAL_CONST_INT64(0); + if (! getHyperValue( nVal, *o3tl::forceAccess<OUString>(rAny) )) + { + throw CannotConvertException( + "invalid STRING value!", + Reference<XInterface>(), aDestinationClass, FailReason::IS_NOT_NUMBER, 0 ); + } + nRet = nVal; + if (nVal >= min && (nVal < 0 || o3tl::make_unsigned(nVal) <= max)) + return nRet; + throw CannotConvertException( + "STRING value out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + + default: + throw CannotConvertException( + "Type " + OUString::number(o3tl::to_underlying(aDestinationClass)) + " is not supported!", + Reference<XInterface>(), aDestinationClass, FailReason::TYPE_NOT_SUPPORTED, 0 ); + } + + if (nRet >= min && (nRet < 0 || o3tl::make_unsigned(nRet) <= max)) + return nRet; + throw CannotConvertException( + "VALUE is out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); +} + + +double TypeConverter_Impl::toDouble( const Any& rAny, double min, double max ) +{ + double fRet; + TypeClass aDestinationClass = rAny.getValueTypeClass(); + + switch (aDestinationClass) + { + // ENUM + case TypeClass_ENUM: + fRet = *static_cast<sal_Int32 const *>(rAny.getValue()); + break; + // BOOL + case TypeClass_BOOLEAN: + fRet = *o3tl::forceAccess<bool>(rAny) ? 1.0 : 0.0; + break; + // CHAR, BYTE + case TypeClass_CHAR: + fRet = *o3tl::forceAccess<sal_Unicode>(rAny); + break; + case TypeClass_BYTE: + fRet = *o3tl::forceAccess<sal_Int8>(rAny); + break; + // SHORT + case TypeClass_SHORT: + fRet = *o3tl::forceAccess<sal_Int16>(rAny); + break; + // UNSIGNED SHORT + case TypeClass_UNSIGNED_SHORT: + fRet = *o3tl::forceAccess<sal_uInt16>(rAny); + break; + // LONG + case TypeClass_LONG: + fRet = *o3tl::forceAccess<sal_Int32>(rAny); + break; + // UNSIGNED LONG + case TypeClass_UNSIGNED_LONG: + fRet = *o3tl::forceAccess<sal_uInt32>(rAny); + break; + // HYPER + case TypeClass_HYPER: + fRet = static_cast<double>(*o3tl::forceAccess<sal_Int64>(rAny)); + break; + // UNSIGNED HYPER + case TypeClass_UNSIGNED_HYPER: + fRet = static_cast<double>(*o3tl::forceAccess<sal_uInt64>(rAny)); + break; + // FLOAT, DOUBLE + case TypeClass_FLOAT: + fRet = *o3tl::forceAccess<float>(rAny); + break; + case TypeClass_DOUBLE: + fRet = *o3tl::forceAccess<double>(rAny); + break; + + // STRING + case TypeClass_STRING: + { + if (! getNumericValue( fRet, *o3tl::forceAccess<OUString>(rAny) )) + { + throw CannotConvertException( + "invalid STRING value!", + Reference<XInterface>(), aDestinationClass, FailReason::IS_NOT_NUMBER, 0 ); + } + break; + } + + default: + throw CannotConvertException( + "Type " + OUString::number(o3tl::to_underlying(aDestinationClass)) + " is not supported!", + Reference< XInterface >(), aDestinationClass, FailReason::TYPE_NOT_SUPPORTED, 0 ); + } + + if (fRet >= min && fRet <= max) + return fRet; + throw CannotConvertException( + "VALUE is out of range!", + Reference< XInterface >(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); +} + + +Any SAL_CALL TypeConverter_Impl::convertTo( const Any& rVal, const Type& aDestType ) +{ + const Type& aSourceType = rVal.getValueType(); + if (aSourceType == aDestType) + return rVal; + + TypeClass aSourceClass = aSourceType.getTypeClass(); + TypeClass aDestinationClass = aDestType.getTypeClass(); + + Any aRet; + + // convert to... + switch (aDestinationClass) + { + // --- to VOID ------------------------------------------------------------------------------ + case TypeClass_VOID: + return Any(); + // --- to ANY ------------------------------------------------------------------------------- + case TypeClass_ANY: + return rVal; + + // --- to STRUCT, EXCEPTION ---------------------------------------------------------- + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + { + // same types or destination type is derived source type? + TypeDescription aSourceTD( aSourceType ); + TypeDescription aDestTD( aDestType ); + if (!typelib_typedescription_isAssignableFrom( aDestTD.get(), aSourceTD.get() )) + { + throw CannotConvertException( + "value is not of same or derived type!", + Reference< XInterface >(), aDestinationClass, + FailReason::SOURCE_IS_NO_DERIVED_TYPE, 0 ); + } + aRet.setValue( rVal.getValue(), aDestTD.get() ); // evtl. .uP.cAsT. + break; + } + // --- to INTERFACE ------------------------------------------------------------------------- + case TypeClass_INTERFACE: + { + if (! rVal.hasValue()) + { + // void -> interface (null) + void * null_ref = nullptr; + // coverity[var_deref_model : FALSE] - null_ref will not be derefed in this case + aRet.setValue( &null_ref, aDestType ); + break; + } + + auto ifc = o3tl::tryAccess<css::uno::Reference<css::uno::XInterface>>( + rVal); + if (!ifc || !ifc->is()) + { + throw CannotConvertException( + "value is not interface", + Reference< XInterface >(), aDestinationClass, FailReason::NO_SUCH_INTERFACE, 0 ); + } + aRet = (*ifc)->queryInterface(aDestType ); + if (! aRet.hasValue()) + { + throw CannotConvertException( + "value does not implement " + aDestType.getTypeName(), + Reference< XInterface >(), aDestinationClass, FailReason::NO_SUCH_INTERFACE, 0 ); + } + break; + } + // --- to SEQUENCE -------------------------------------------------------------------------- + case TypeClass_SEQUENCE: + { + if (aSourceClass==TypeClass_SEQUENCE) + { + if( aSourceType == aDestType ) + return rVal; + + TypeDescription aSourceTD( aSourceType ); + TypeDescription aDestTD( aDestType ); + // For a sequence type notation "[]...", SequenceTypeDescription in + // cppuhelper/source/typemanager.cxx resolves the "..." component type notation part + // only lazily, so it could happen here that bad user input (e.g., "[]" or "[]foo" from + // a Basic script CreateUnoValue call) leads to a bad but as-of-yet undetected + // aDestType, so check it here; this is less likely an issue for the non-sequence type + // classes, whose notation is not resolved lazily based on their syntax: + if (!aDestTD.is()) { + throw css::lang::IllegalArgumentException( + "Bad XTypeConverter::convertTo destination " + aDestType.getTypeName(), + getXWeak(), 1); + } + typelib_TypeDescription * pSourceElementTD = nullptr; + TYPELIB_DANGER_GET( + &pSourceElementTD, + reinterpret_cast<typelib_IndirectTypeDescription *>(aSourceTD.get())->pType ); + typelib_TypeDescription * pDestElementTD = nullptr; + TYPELIB_DANGER_GET( + &pDestElementTD, + reinterpret_cast<typelib_IndirectTypeDescription *>(aDestTD.get())->pType ); + + sal_uInt32 nPos = (*static_cast<const uno_Sequence * const *>(rVal.getValue()))->nElements; + uno_Sequence * pRet = nullptr; + uno_sequence_construct( + &pRet, aDestTD.get(), nullptr, nPos, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + aRet.setValue( &pRet, aDestTD.get() ); + uno_destructData( + &pRet, aDestTD.get(), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + // decr ref count + + char * pDestElements = (*static_cast<uno_Sequence * const *>(aRet.getValue()))->elements; + const char * pSourceElements = + (*static_cast<const uno_Sequence * const *>(rVal.getValue()))->elements; + + while (nPos--) + { + char * pDestPos = pDestElements + (nPos * pDestElementTD->nSize); + const char * pSourcePos = pSourceElements + (nPos * pSourceElementTD->nSize); + + Any aElement( + convertTo( Any( pSourcePos, pSourceElementTD ), pDestElementTD->pWeakRef ) ); + + if (!uno_assignData( + pDestPos, pDestElementTD, + (pDestElementTD->eTypeClass == typelib_TypeClass_ANY + ? &aElement + : const_cast< void * >( aElement.getValue() )), + pDestElementTD, + reinterpret_cast< uno_QueryInterfaceFunc >( + cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) )) + { + OSL_ASSERT( false ); + } + } + TYPELIB_DANGER_RELEASE( pDestElementTD ); + TYPELIB_DANGER_RELEASE( pSourceElementTD ); + } + break; + } + // --- to ENUM ------------------------------------------------------------------------------ + case TypeClass_ENUM: + { + TypeDescription aEnumTD( aDestType ); + aEnumTD.makeComplete(); + sal_Int32 nPos = -1; + + if (aSourceClass==TypeClass_STRING) + { + for ( nPos = reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->nEnumValues; nPos--; ) + { + if (o3tl::forceAccess<OUString>(rVal)->equalsIgnoreAsciiCase( + OUString::unacquired(&reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->ppEnumNames[nPos]) )) + break; + } + } + else if (aSourceClass!=TypeClass_ENUM && // exclude some unwanted types for toHyper() + aSourceClass!=TypeClass_BOOLEAN && + aSourceClass!=TypeClass_CHAR) + { + sal_Int32 nEnumValue = static_cast<sal_Int32>(toHyper( rVal, -sal_Int64(0x80000000), 0x7fffffff )); + for ( nPos = reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->nEnumValues; nPos--; ) + { + if (nEnumValue == reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->pEnumValues[nPos]) + break; + } + } + + if (nPos < 0) + { + throw CannotConvertException( + "value cannot be converted to demanded ENUM!", + Reference< XInterface >(), aDestinationClass, FailReason::IS_NOT_ENUM, 0 ); + } + + aRet.setValue( + &reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->pEnumValues[nPos], + aEnumTD.get() ); + + break; + } + + default: + // else simple type conversion possible? + try + { + aRet = convertToSimpleType( rVal, aDestinationClass ); + } + catch (IllegalArgumentException &) + { + // ...FailReason::INVALID is thrown + } + } + + if (aRet.hasValue()) + return aRet; + + throw CannotConvertException( + "conversion not possible!", + Reference< XInterface >(), aDestinationClass, FailReason::INVALID, 0 ); +} + + +Any TypeConverter_Impl::convertToSimpleType( const Any& rVal, TypeClass aDestinationClass ) +{ + switch (aDestinationClass) + { + // only simple Conversion of _simple_ types + case TypeClass_VOID: + case TypeClass_BOOLEAN: + case TypeClass_BYTE: + case TypeClass_SHORT: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_LONG: + case TypeClass_UNSIGNED_LONG: + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + case TypeClass_CHAR: + case TypeClass_STRING: + case TypeClass_ANY: + break; + + default: + throw IllegalArgumentException( + "destination type is not simple!", + Reference< XInterface >(), sal_Int16(1) ); + } + + const Type& aSourceType = rVal.getValueType(); + TypeClass aSourceClass = aSourceType.getTypeClass(); + if (aDestinationClass == aSourceClass) + return rVal; + + Any aRet; + + // Convert to... + switch (aDestinationClass) + { + // --- to VOID ------------------------------------------------------------------------------ + case TypeClass_VOID: + return Any(); + + // --- to ANY ------------------------------------------------------------------------------- + case TypeClass_ANY: + return rVal; + + // --- to BOOL ------------------------------------------------------------------------------ + case TypeClass_BOOLEAN: + switch (aSourceClass) + { + default: + aRet <<= (toDouble( rVal ) != 0.0); + break; + case TypeClass_ENUM: // exclude enums + break; + + case TypeClass_STRING: + { + const OUString & aStr = *o3tl::forceAccess<OUString>(rVal); + if ( aStr == "0" || aStr.equalsIgnoreAsciiCase( "false" )) + { + aRet <<= false; + } + else if ( aStr == "1" || aStr.equalsIgnoreAsciiCase( "true" )) + { + aRet <<= true; + } + else + { + throw CannotConvertException( + "STRING has no boolean value, " + aStr, + Reference< XInterface >(), aDestinationClass, FailReason::IS_NOT_BOOL, 0 ); + } + } + } + break; + + // --- to CHAR, BYTE ------------------------------------------------------------------------ + case TypeClass_CHAR: + { + if (aSourceClass==TypeClass_STRING) + { + auto const s = o3tl::forceAccess<OUString>(rVal); + if (s->getLength() == 1) // single char + aRet <<= (*s)[0]; + } + else if (aSourceClass!=TypeClass_ENUM && // exclude enums, chars + aSourceClass!=TypeClass_CHAR) + { + aRet <<= sal_Unicode(toHyper( rVal, 0, 0xffff )); // range + } + break; + } + case TypeClass_BYTE: + aRet <<= static_cast<sal_Int8>( toHyper( rVal, -sal_Int64(0x80), 0x7f ) ); + break; + + // --- to SHORT, UNSIGNED SHORT ------------------------------------------------------------- + case TypeClass_SHORT: + aRet <<= static_cast<sal_Int16>( toHyper( rVal, -sal_Int64(0x8000), 0x7fff ) ); + break; + case TypeClass_UNSIGNED_SHORT: + aRet <<= static_cast<sal_uInt16>( toHyper( rVal, 0, 0xffff ) ); + break; + + // --- to LONG, UNSIGNED LONG --------------------------------------------------------------- + case TypeClass_LONG: + aRet <<= static_cast<sal_Int32>( toHyper( rVal, -sal_Int64(0x80000000), 0x7fffffff ) ); + break; + case TypeClass_UNSIGNED_LONG: + aRet <<= static_cast<sal_uInt32>( toHyper( rVal, 0, 0xffffffff ) ); + break; + + // --- to HYPER, UNSIGNED HYPER-------------------------------------------- + case TypeClass_HYPER: + aRet <<= toHyper( rVal, SAL_MIN_INT64, SAL_MAX_INT64 ); + break; + case TypeClass_UNSIGNED_HYPER: + aRet <<= static_cast<sal_uInt64>( toHyper( rVal, 0 ) ); + break; + + // --- to FLOAT, DOUBLE --------------------------------------------------------------------- + case TypeClass_FLOAT: + aRet <<= static_cast<float>( toDouble( rVal, -FLT_MAX, FLT_MAX ) ); + break; + case TypeClass_DOUBLE: + aRet <<= toDouble( rVal, -DBL_MAX, DBL_MAX ); + break; + + // --- to STRING ---------------------------------------------------------------------------- + case TypeClass_STRING: + switch (aSourceClass) + { + case TypeClass_ENUM: + { + TypeDescription aEnumTD( aSourceType ); + aEnumTD.makeComplete(); + sal_Int32 nPos; + sal_Int32 nEnumValue = *static_cast<sal_Int32 const *>(rVal.getValue()); + for ( nPos = reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->nEnumValues; nPos--; ) + { + if (nEnumValue == reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->pEnumValues[nPos]) + break; + } + if (nPos < 0) + { + throw CannotConvertException( + "value is not ENUM!", + Reference< XInterface >(), aDestinationClass, FailReason::IS_NOT_ENUM, 0 ); + } + + aRet <<= OUString::unacquired( + &reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->ppEnumNames[nPos]); + + break; + } + + case TypeClass_BOOLEAN: + aRet <<= *o3tl::forceAccess<bool>(rVal) ? + OUString("true") : + OUString("false"); + break; + case TypeClass_CHAR: + aRet <<= OUString(*o3tl::forceAccess<sal_Unicode>(rVal)); + break; + + case TypeClass_BYTE: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int8>(rVal) ); + break; + case TypeClass_SHORT: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int16>(rVal) ); + break; + case TypeClass_UNSIGNED_SHORT: + aRet <<= OUString::number( *o3tl::forceAccess<sal_uInt16>(rVal) ); + break; + case TypeClass_LONG: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int32>(rVal) ); + break; + case TypeClass_UNSIGNED_LONG: + aRet <<= OUString::number( *o3tl::forceAccess<sal_uInt32>(rVal) ); + break; + case TypeClass_HYPER: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int64>(rVal) ); + break; +// case TypeClass_UNSIGNED_HYPER: +// aRet <<= OUString::valueOf( (sal_Int64)*(sal_uInt64 const *)rVal.getValue() ); +// break; + // handle unsigned hyper like double + + default: + aRet <<= OUString::number( toDouble( rVal ) ); + } + break; + + default: + OSL_ASSERT(false); + break; + } + + if (aRet.hasValue()) + return aRet; + + throw CannotConvertException( + "conversion not possible!", + Reference< XInterface >(), aDestinationClass, FailReason::INVALID, 0 ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_stoc_TypeConverter_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new stoc_tcv::TypeConverter_Impl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/ExternalUriReferenceTranslator.cxx b/stoc/source/uriproc/ExternalUriReferenceTranslator.cxx new file mode 100644 index 0000000000..b9e2ca4e99 --- /dev/null +++ b/stoc/source/uriproc/ExternalUriReferenceTranslator.cxx @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/thread.h> +#include <rtl/string.h> +#include <rtl/textenc.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class XInterface; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace { + +class Translator: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XExternalUriReferenceTranslator> +{ +public: + Translator() {} + + Translator(const Translator&) = delete; + Translator& operator=(const Translator&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual OUString SAL_CALL + translateToInternal(OUString const & externalUriReference) override; + + virtual OUString SAL_CALL + translateToExternal(OUString const & internalUriReference) override; + +private: + virtual ~Translator() override {} +}; + +OUString Translator::getImplementationName() +{ + return "com.sun.star.comp.uri.ExternalUriReferenceTranslator"; +} + +sal_Bool Translator::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Translator::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > s { "com.sun.star.uri.ExternalUriReferenceTranslator" }; + return s; +} + +OUString Translator::translateToInternal( + OUString const & externalUriReference) +{ + if (!externalUriReference.matchIgnoreAsciiCase("file:/")) + { + return externalUriReference; + } + sal_Int32 i = RTL_CONSTASCII_LENGTH("file:"); + OUStringBuffer buf(128); + buf.append(externalUriReference.subView(0, i)); + // Some environments (e.g., Java) produce illegal file URLs without an + // authority part; treat them as having an empty authority part: + if (!externalUriReference.match("//", i)) + { + buf.append("//"); + } + rtl_TextEncoding encoding = osl_getThreadTextEncoding(); + for (bool path = true;;) { + sal_Int32 j = i; + while (j != externalUriReference.getLength() + && externalUriReference[j] != '#' + && (!path || externalUriReference[j] != '/')) + { + ++j; + } + if (j != i) { + OUString seg( + rtl::Uri::encode( + rtl::Uri::decode( + externalUriReference.copy(i, j - i), + rtl_UriDecodeStrict, encoding), + rtl_UriCharClassPchar, rtl_UriEncodeStrict, + RTL_TEXTENCODING_UTF8)); + if (seg.isEmpty()) { + return OUString(); + } + buf.append(seg); + } + if (j == externalUriReference.getLength()) { + break; + } + buf.append(externalUriReference[j]); + path = externalUriReference[j] == '/'; + i = j + 1; + } + return buf.makeStringAndClear(); +} + +OUString Translator::translateToExternal( + OUString const & internalUriReference) +{ + if (!internalUriReference.matchIgnoreAsciiCase("file://")) + { + return internalUriReference; + } + sal_Int32 i = RTL_CONSTASCII_LENGTH("file://"); + OUStringBuffer buf(128); + buf.append(internalUriReference.subView(0, i)); + rtl_TextEncoding encoding = osl_getThreadTextEncoding(); + for (bool path = true;;) { + sal_Int32 j = i; + while (j != internalUriReference.getLength() + && internalUriReference[j] != '#' + && (!path || internalUriReference[j] != '/')) + { + ++j; + } + if (j != i) { + // Use rtl_UriDecodeToIuri -> rtl_UriEncodeStrictKeepEscapes instead + // of rtl_UriDecodeStrict -> rtl_UriEncodeStrict, so that spurious + // non--UTF-8 octets like "%FE" are copied verbatim: + OUString seg( + rtl::Uri::encode( + rtl::Uri::decode( + internalUriReference.copy(i, j - i), + rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8), + rtl_UriCharClassPchar, rtl_UriEncodeStrictKeepEscapes, + encoding)); + if (seg.isEmpty()) { + return OUString(); + } + buf.append(seg); + } + if (j == internalUriReference.getLength()) { + break; + } + buf.append(internalUriReference[j]); + path = internalUriReference[j] == '/'; + i = j + 1; + } + return buf.makeStringAndClear(); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_ExternalUriReferenceTranslator_get_implementation(css::uno::XComponentContext* , + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new Translator); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriReference.cxx b/stoc/source/uriproc/UriReference.cxx new file mode 100644 index 0000000000..fc27201414 --- /dev/null +++ b/stoc/source/uriproc/UriReference.cxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include "UriReference.hxx" + +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <utility> +#include <sal/types.h> + +using stoc::uriproc::UriReference; + +UriReference::UriReference( + OUString scheme, bool bHasAuthority, + OUString const & authority, OUString path, + bool bHasQuery, OUString const & query): + m_path(std::move(path)), + m_scheme(std::move(scheme)), + m_authority(authority), + m_query(query), + m_hasAuthority(bHasAuthority), + m_hasQuery(bHasQuery), + m_hasFragment(false) +{ + assert(authority.isEmpty() || bHasAuthority); + assert(query.isEmpty() || bHasQuery); +} + +UriReference::~UriReference() {} + +OUString UriReference::getUriReference() +{ + std::lock_guard g(m_mutex); + OUStringBuffer buf(128); + if (!m_scheme.isEmpty()) { + buf.append(m_scheme + ":"); + } + appendSchemeSpecificPart(buf); + if (m_hasFragment) { + buf.append("#" + m_fragment); + } + return buf.makeStringAndClear(); +} + +bool UriReference::isAbsolute() const { + return !m_scheme.isEmpty(); +} + + +OUString UriReference::getSchemeSpecificPart() +{ + std::lock_guard g(m_mutex); + OUStringBuffer buf; + appendSchemeSpecificPart(buf); + return buf.makeStringAndClear(); +} + +bool UriReference::isHierarchical() { + std::lock_guard g(m_mutex); + return m_scheme.isEmpty() || m_hasAuthority || m_path.startsWith("/"); +} + +bool UriReference::hasAuthority() const { + return m_hasAuthority; +} + +const OUString& UriReference::getAuthority() const { + return m_authority; +} + +OUString UriReference::getPath() { + std::lock_guard g(m_mutex); + return m_path; +} + +bool UriReference::hasRelativePath() { + std::lock_guard g(m_mutex); + return !m_hasAuthority + && (m_path.isEmpty() || m_path[0] != '/'); +} + +sal_Int32 UriReference::getPathSegmentCount() +{ + std::lock_guard g(m_mutex); + if (m_path.isEmpty()) { + return 0; + } else { + sal_Int32 n = m_path[0] == '/' ? 0 : 1; + for (sal_Int32 i = 0;; ++i) { + i = m_path.indexOf('/', i); + if (i < 0) { + break; + } + ++n; + } + return n; + } +} + +OUString UriReference::getPathSegment(sal_Int32 index) +{ + std::lock_guard g(m_mutex); + if (!m_path.isEmpty() && index >= 0) { + for (sal_Int32 i = m_path[0] == '/' ? 1 : 0;; ++i) { + if (index-- == 0) { + sal_Int32 j = m_path.indexOf('/', i); + return j < 0 ? m_path.copy(i) : m_path.copy(i, j - i); + } + i = m_path.indexOf('/', i); + if (i < 0) { + break; + } + } + } + return OUString(); +} + +bool UriReference::hasQuery() const { + return m_hasQuery; +} + +const OUString& UriReference::getQuery() const { + return m_query; +} + +bool UriReference::hasFragment() { + std::lock_guard g(m_mutex); + return m_hasFragment; +} + +OUString UriReference::getFragment() { + std::lock_guard g(m_mutex); + return m_fragment; +} + +void UriReference::setFragment(OUString const & fragment) +{ + std::lock_guard g(m_mutex); + m_hasFragment = true; + m_fragment = fragment; +} + +void UriReference::clearFragment() { + std::lock_guard g(m_mutex); + m_hasFragment = false; + m_fragment.clear(); +} + +void UriReference::appendSchemeSpecificPart(OUStringBuffer & buffer) const +{ + if (m_hasAuthority) { + buffer.append("//"); + buffer.append(m_authority); + } + buffer.append(m_path); + if (m_hasQuery) { + buffer.append('?'); + buffer.append(m_query); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriReference.hxx b/stoc/source/uriproc/UriReference.hxx new file mode 100644 index 0000000000..1b373f56a7 --- /dev/null +++ b/stoc/source/uriproc/UriReference.hxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_STOC_SOURCE_URIPROC_URIREFERENCE_HXX +#define INCLUDED_STOC_SOURCE_URIPROC_URIREFERENCE_HXX + +#include <mutex> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <rtl/ustrbuf.hxx> + +namespace stoc::uriproc { + +class UriReference { +public: + UriReference( + OUString scheme, bool hasAuthority, + OUString const & authority, OUString path, + bool hasQuery, OUString const & query); + + ~UriReference(); + + /// @throws css::uno::RuntimeException + OUString getUriReference(); + + /// @throws css::uno::RuntimeException + bool isAbsolute() const; + + /// @throws css::uno::RuntimeException + const OUString& getScheme() const { return m_scheme;} + + /// @throws css::uno::RuntimeException + OUString getSchemeSpecificPart(); + + /// @throws css::uno::RuntimeException + bool isHierarchical(); + + /// @throws css::uno::RuntimeException + bool hasAuthority() const; + + /// @throws css::uno::RuntimeException + const OUString& getAuthority() const; + + /// @throws css::uno::RuntimeException + OUString getPath(); + + /// @throws css::uno::RuntimeException + bool hasRelativePath(); + + /// @throws css::uno::RuntimeException + sal_Int32 getPathSegmentCount(); + + /// @throws css::uno::RuntimeException + OUString getPathSegment(sal_Int32 index); + + /// @throws css::uno::RuntimeException + bool hasQuery() const; + + /// @throws css::uno::RuntimeException + const OUString& getQuery() const; + + /// @throws css::uno::RuntimeException + bool hasFragment(); + + /// @throws css::uno::RuntimeException + OUString getFragment(); + + /// @throws css::uno::RuntimeException + void setFragment(OUString const & fragment); + + /// @throws css::uno::RuntimeException + void clearFragment(); + + std::mutex m_mutex; + OUString m_path; + +private: + UriReference(UriReference const &) = delete; + void operator =(UriReference const &) = delete; + + void appendSchemeSpecificPart(OUStringBuffer & buffer) const; + + OUString m_scheme; + OUString m_authority; + OUString m_query; + OUString m_fragment; + bool m_hasAuthority; + bool m_hasQuery; + bool m_hasFragment; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriReferenceFactory.cxx b/stoc/source/uriproc/UriReferenceFactory.cxx new file mode 100644 index 0000000000..2573917713 --- /dev/null +++ b/stoc/source/uriproc/UriReferenceFactory.cxx @@ -0,0 +1,701 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <string_view> +#include <utility> +#include <vector> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/uri/XUriSchemeParser.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +#include "UriReference.hxx" + +namespace { + +bool equalIgnoreEscapeCase(std::u16string_view s1, std::u16string_view s2) { + if (s1.size() == s2.size()) { + for (size_t i = 0; i < s1.size();) { + if (s1[i] == '%' && s2[i] == '%' && s1.size() - i > 2 + && rtl::isAsciiHexDigit(s1[i + 1]) + && rtl::isAsciiHexDigit(s1[i + 2]) + && rtl::isAsciiHexDigit(s2[i + 1]) + && rtl::isAsciiHexDigit(s2[i + 2]) + && rtl::compareIgnoreAsciiCase(s1[i + 1], s2[i + 1]) == 0 + && rtl::compareIgnoreAsciiCase(s1[i + 2], s2[i + 2]) == 0) + { + i += 3; + } else if (s1[i] != s2[i]) { + return false; + } else { + ++i; + } + } + return true; + } else { + return false; + } +} + +sal_Int32 parseScheme(std::u16string_view uriReference) { + if (uriReference.size() >= 2 && rtl::isAsciiAlpha(uriReference[0])) { + for (size_t i = 0; i < uriReference.size(); ++i) { + sal_Unicode c = uriReference[i]; + if (c == ':') { + return i; + } else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c) + && c != '+' && c != '-' && c != '.') + { + break; + } + } + } + return -1; +} + +class UriReference: + public cppu::WeakImplHelper<css::uri::XUriReference> +{ +public: + UriReference( + OUString const & scheme, bool bHasAuthority, + OUString const & authority, OUString const & path, + bool bHasQuery, OUString const & query): + m_base( + scheme, bHasAuthority, authority, path, bHasQuery, + query) + {} + + UriReference(const UriReference&) = delete; + UriReference& operator=(const UriReference&) = delete; + + virtual OUString SAL_CALL getUriReference() override + { return m_base.getUriReference(); } + + virtual sal_Bool SAL_CALL isAbsolute() override + { return m_base.isAbsolute(); } + + virtual OUString SAL_CALL getScheme() override + { return m_base.getScheme(); } + + virtual OUString SAL_CALL getSchemeSpecificPart() override + { return m_base.getSchemeSpecificPart(); } + + virtual sal_Bool SAL_CALL isHierarchical() override + { return m_base.isHierarchical(); } + + virtual sal_Bool SAL_CALL hasAuthority() override + { return m_base.hasAuthority(); } + + virtual OUString SAL_CALL getAuthority() override + { return m_base.getAuthority(); } + + virtual OUString SAL_CALL getPath() override + { return m_base.getPath(); } + + virtual sal_Bool SAL_CALL hasRelativePath() override + { return m_base.hasRelativePath(); } + + virtual sal_Int32 SAL_CALL getPathSegmentCount() override + { return m_base.getPathSegmentCount(); } + + virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override + { return m_base.getPathSegment(index); } + + virtual sal_Bool SAL_CALL hasQuery() override + { return m_base.hasQuery(); } + + virtual OUString SAL_CALL getQuery() override + { return m_base.getQuery(); } + + virtual sal_Bool SAL_CALL hasFragment() override + { return m_base.hasFragment(); } + + virtual OUString SAL_CALL getFragment() override + { return m_base.getFragment(); } + + virtual void SAL_CALL setFragment(OUString const & fragment) override + { m_base.setFragment(fragment); } + + virtual void SAL_CALL clearFragment() override + { m_base.clearFragment(); } + +private: + virtual ~UriReference() override {} + + stoc::uriproc::UriReference m_base; +}; + +css::uno::Reference< css::uri::XUriReference > parseGeneric( + OUString const & scheme, OUString const & schemeSpecificPart) +{ + sal_Int32 len = schemeSpecificPart.getLength(); + sal_Int32 i = 0; + bool hasAuthority = false; + OUString authority; + if (len - i >= 2 && schemeSpecificPart[i] == '/' + && schemeSpecificPart[i + 1] == '/') + { + i += 2; + sal_Int32 n = i; + while (i < len && schemeSpecificPart[i] != '/' + && schemeSpecificPart[i] != '?') { + ++i; + } + hasAuthority = true; + authority = schemeSpecificPart.copy(n, i - n); + } + sal_Int32 n = i; + i = schemeSpecificPart.indexOf('?', i); + if (i == -1) { + i = len; + } + OUString path = schemeSpecificPart.copy(n, i - n); + bool hasQuery = false; + OUString query; + if (i != len) { + hasQuery = true; + query = schemeSpecificPart.copy(i + 1); + } + return new UriReference( + scheme, hasAuthority, authority, path, hasQuery, query); +} + +struct Segment { + bool leadingSlash; + bool excessParent; + std::u16string_view segment; + + Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment): + leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {} +}; + +std::pair<std::vector<Segment>, bool> processSegments( + std::u16string_view first, std::u16string_view second, bool processSpecialSegments) +{ + std::vector<Segment> segments; + bool processed = false; + std::u16string_view const * half = &first; + // later checks for `half == &first` and `half == &second` rely on the fact that `first` and + // `second` are passed by value, in case a caller passes the same object for both arguments + std::size_t index = 0; + bool slash = false; + if (index == half->length()) { + half = &second; + index = 0; + } + if (index != half->length()) { + if ((*half)[index] == u'/') { + slash = true; + ++index; + } + for (;;) { + if (index == half->length() && half == &first) { + half = &second; + index = 0; + } + if (index == half->length()) { + if (slash) { + segments.emplace_back(true, false, std::u16string_view()); + } + break; + } + auto const n = std::min(half->find(u'/', index), half->length()); + auto const leadingSlash = slash; + auto const segment = half->substr(index, n - index); + auto const process = processSpecialSegments || half == &second; + index = n; + slash = false; + if (index == half->length() && half == &first) { + half = &second; + index = 0; + } + if (index != half->length() && (*half)[index] == u'/') { + slash = true; + ++index; + } + if (process) { + if (segment == u".") { + slash = leadingSlash; + processed = true; + continue; + } else if (segment == u"..") { + if (segments.empty() || segments.back().excessParent) { + segments.emplace_back(leadingSlash, true, segment); + } else { + if (leadingSlash) { + segments.pop_back(); + } + slash = leadingSlash; + } + processed = true; + continue; + } + } + segments.emplace_back(leadingSlash, false, segment); + } + } + return {segments, processed}; +} + +class Factory: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XUriReferenceFactory> +{ +public: + explicit Factory( + css::uno::Reference< css::uno::XComponentContext > context): + m_context(std::move(context)) {} + + Factory(const Factory&) = delete; + Factory& operator=(const Factory&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + parse(OUString const & uriReference) override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + makeAbsolute( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool processAdditionalSpecialSegments, + css::uri::RelativeUriExcessParentSegments excessParentSegments) override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + makeRelative( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool preferAuthorityOverRelativePath, + sal_Bool preferAbsoluteOverRelativePath, + sal_Bool encodeRetainedSpecialSegments) override; + +private: + virtual ~Factory() override {} + + css::uno::Reference< css::uri::XUriReference > clone( + css::uno::Reference< css::uri::XUriReference > const & uriReference) + { return parse(uriReference->getUriReference()); } + + css::uno::Reference< css::uno::XComponentContext > m_context; +}; + +OUString Factory::getImplementationName() +{ + return "com.sun.star.comp.uri.UriReferenceFactory"; +} + +sal_Bool Factory::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Factory::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > s { "com.sun.star.uri.UriReferenceFactory" }; + return s; +} + +css::uno::Reference< css::uri::XUriReference > Factory::parse( + OUString const & uriReference) +{ + sal_Int32 fragment = uriReference.indexOf('#'); + if (fragment == -1) { + fragment = uriReference.getLength(); + } + OUString scheme; + OUString schemeSpecificPart; + OUString serviceName; + sal_Int32 n = parseScheme(uriReference); + assert(n < fragment); + if (n >= 0) { + scheme = uriReference.copy(0, n); + schemeSpecificPart = uriReference.copy(n + 1, fragment - (n + 1)); + OUStringBuffer buf(128); + buf.append("com.sun.star.uri.UriSchemeParser_"); + for (sal_Int32 i = 0; i < scheme.getLength(); ++i) { + sal_Unicode c = scheme[i]; + if (rtl::isAsciiUpperCase(c)) { + buf.append(static_cast<sal_Unicode>(rtl::toAsciiLowerCase(c))); + } else if (c == '+') { + buf.append("PLUS"); + } else if (c == '-') { + buf.append("HYPHEN"); + } else if (c == '.') { + buf.append("DOT"); + } else { + assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c)); + buf.append(c); + } + } + serviceName = buf.makeStringAndClear(); + } else { + schemeSpecificPart = uriReference.copy(0, fragment); + } + css::uno::Reference< css::uri::XUriSchemeParser > parser; + if (!serviceName.isEmpty()) { + css::uno::Reference< css::lang::XMultiComponentFactory > factory( + m_context->getServiceManager()); + if (factory.is()) { + css::uno::Reference< css::uno::XInterface > service; + try { + service = factory->createInstanceWithContext( + serviceName, m_context); + } catch (css::uno::RuntimeException &) { + throw; + } catch (const css::uno::Exception &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "creating service " + serviceName, + getXWeak(), + anyEx); + } + if (service.is()) { + parser.set( service, css::uno::UNO_QUERY_THROW); + } + } + } + css::uno::Reference< css::uri::XUriReference > uriRef( + parser.is() + ? parser->parse(scheme, schemeSpecificPart) + : parseGeneric(scheme, schemeSpecificPart)); + if (uriRef.is() && fragment != uriReference.getLength()) { + uriRef->setFragment(uriReference.copy(fragment + 1)); + } + return uriRef; +} + +css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool processAdditionalSpecialSegments, + css::uri::RelativeUriExcessParentSegments excessParentSegments) +{ + if (!baseUriReference.is() || !baseUriReference->isAbsolute() + || !uriReference.is()) { + return nullptr; + } else if (uriReference->isAbsolute()) { + if (processAdditionalSpecialSegments) { + auto const path = uriReference->getPath(); + auto [segments, proc] = processSegments(path, {}, true); + if (proc) { + OUStringBuffer abs(uriReference->getScheme() + ":"); + if (uriReference->hasAuthority()) { + abs.append("//" + uriReference->getAuthority()); + } + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + if (uriReference->hasQuery()) { + abs.append("?" + uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append("#" + uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } + } + return clone(uriReference); + } else if (!uriReference->hasAuthority() + && uriReference->getPath().isEmpty()) { + OUStringBuffer abs(baseUriReference->getScheme() + ":"); + if (baseUriReference->hasAuthority()) { + abs.append("//" + baseUriReference->getAuthority()); + } + abs.append(baseUriReference->getPath()); + if (uriReference->hasQuery()) { + abs.append("?" + uriReference->getQuery()); + } else if (baseUriReference->hasQuery()) { + abs.append("?" + baseUriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append("#" + uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } else { + OUStringBuffer abs(128); + abs.append(baseUriReference->getScheme() + ":"); + if (uriReference->hasAuthority()) { + abs.append("//" + uriReference->getAuthority()); + } else if (baseUriReference->hasAuthority()) { + abs.append("//" + baseUriReference->getAuthority()); + } + if (uriReference->hasRelativePath()) { + auto path1 = baseUriReference->getPath(); + if (path1.isEmpty()) { + if (baseUriReference->hasAuthority()) { + path1 = "/"; + } + } else { + path1 = path1.copy(0, path1.lastIndexOf('/') + 1); + } + auto const path2 = uriReference->getPath(); + auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments); + (void)_; + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + } else { + bool processed = false; + if (processAdditionalSpecialSegments) { + auto const path = uriReference->getPath(); + auto [segments, proc] = processSegments(path, {}, true); + if (proc) { + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + processed = true; + } + } + if (!processed) { + abs.append(uriReference->getPath()); + } + } + if (uriReference->hasQuery()) { + abs.append("?" + uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append("#" + uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } +} + +css::uno::Reference< css::uri::XUriReference > Factory::makeRelative( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool preferAuthorityOverRelativePath, + sal_Bool preferAbsoluteOverRelativePath, + sal_Bool encodeRetainedSpecialSegments) +{ + if (!baseUriReference.is() || !baseUriReference->isAbsolute() + || !uriReference.is()) { + return nullptr; + } else if (!uriReference->isAbsolute() || uriReference->hasRelativePath() + || !baseUriReference->getScheme().equalsIgnoreAsciiCase( + uriReference->getScheme())) { + return clone(uriReference); + } else { + OUStringBuffer rel(128); + bool omitQuery = false; + if ((baseUriReference->hasAuthority() != uriReference->hasAuthority()) + || !equalIgnoreEscapeCase( + baseUriReference->getAuthority(), + uriReference->getAuthority())) + { + if (uriReference->hasAuthority()) { + rel.append("//" + uriReference->getAuthority()); + } + rel.append(uriReference->getPath()); + } else if ((equalIgnoreEscapeCase( + baseUriReference->getPath(), uriReference->getPath()) + || (baseUriReference->getPath() == "/" + && uriReference->getPath().isEmpty())) + && baseUriReference->hasQuery() == uriReference->hasQuery() + && equalIgnoreEscapeCase( + baseUriReference->getQuery(), uriReference->getQuery())) + { + omitQuery = true; + } else { + sal_Int32 count1 = std::max< sal_Int32 >( + baseUriReference->getPathSegmentCount(), 1); + sal_Int32 count2 = std::max< sal_Int32 >( + uriReference->getPathSegmentCount(), 1); + sal_Int32 i = 0; + for (; i < std::min(count1, count2) - 1; ++i) { + if (!equalIgnoreEscapeCase( + baseUriReference->getPathSegment(i), + uriReference->getPathSegment(i))) + { + break; + } + } + if (i == 0 + && (preferAbsoluteOverRelativePath || uriReference->hasQuery()) + && (preferAuthorityOverRelativePath + || !uriReference->getPath().startsWith("//"))) + { + if (uriReference->getPath().isEmpty()) { + if (!baseUriReference->getPath().isEmpty() + && baseUriReference->getPath() != "/") + { + rel.append('/'); + } + } else if (uriReference->getPath() == "/") { + if (baseUriReference->getPath().isEmpty() + || baseUriReference->getPath() != "/") + { + rel.append('/'); + } + } else { + if (uriReference->getPath().startsWith("//")) { + assert(uriReference->hasAuthority()); + rel.append("//" + uriReference->getAuthority()); + } + rel.append(uriReference->getPath()); + } + } else { + bool segments = false; + for (sal_Int32 j = i; j < count1 - 1; ++j) { + if (segments) { + rel.append('/'); + } + rel.append(".."); + segments = true; + } + if (i < count2 - 1 + || (!uriReference->getPathSegment(count2 - 1).isEmpty())) + { + if (!segments + && (uriReference->getPathSegment(i).isEmpty() + || (parseScheme(uriReference->getPathSegment(i)) + >= 0))) + { + rel.append('.'); + segments = true; + } + for (; i < count2; ++i) { + if (segments) { + rel.append('/'); + } + OUString s(uriReference->getPathSegment(i)); + if (encodeRetainedSpecialSegments && s == ".") { + rel.append("%2E"); + } else if (encodeRetainedSpecialSegments && s == "..") { + rel.append("%2E%2E"); + } else { + rel.append(s); + } + segments = true; + } + } + } + } + if (!omitQuery && uriReference->hasQuery()) { + rel.append("?" + uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + rel.append("#" + uriReference->getFragment()); + } + return parse(rel.makeStringAndClear()); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext, + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new Factory(rxContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand.cxx b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand.cxx new file mode 100644 index 0000000000..dde6fcb060 --- /dev/null +++ b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand.cxx @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/XUriSchemeParser.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrlReference.hpp> +#include <com/sun/star/util/XMacroExpander.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/textenc.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +#include "UriReference.hxx" + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { class XInterface; } +namespace com::sun::star::uri { class XUriReference; } + +namespace { + +bool parseSchemeSpecificPart(OUString const & part) { + // Liberally accepts both an empty opaque_part and an opaque_part that + // starts with a non-escaped "/": + return part.isEmpty() + || (!::rtl::Uri::decode(part, ::rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8).isEmpty()); +} + +class UrlReference: + public ::cppu::WeakImplHelper<css::uri::XVndSunStarExpandUrlReference> +{ +public: + UrlReference(OUString const & scheme, OUString const & path): + base_( + scheme, false, OUString(), path, false, + OUString()) + {} + + UrlReference(const UrlReference&) = delete; + UrlReference& operator=(const UrlReference&) = delete; + + virtual OUString SAL_CALL getUriReference() override + { return base_.getUriReference(); } + + virtual sal_Bool SAL_CALL isAbsolute() override + { return base_.isAbsolute(); } + + virtual OUString SAL_CALL getScheme() override + { return base_.getScheme(); } + + virtual OUString SAL_CALL getSchemeSpecificPart() override + { return base_.getSchemeSpecificPart(); } + + virtual sal_Bool SAL_CALL isHierarchical() override + { return base_.isHierarchical(); } + + virtual sal_Bool SAL_CALL hasAuthority() override + { return base_.hasAuthority(); } + + virtual OUString SAL_CALL getAuthority() override + { return base_.getAuthority(); } + + virtual OUString SAL_CALL getPath() override + { return base_.getPath(); } + + virtual sal_Bool SAL_CALL hasRelativePath() override + { return base_.hasRelativePath(); } + + virtual ::sal_Int32 SAL_CALL getPathSegmentCount() override + { return base_.getPathSegmentCount(); } + + virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override + { return base_.getPathSegment(index); } + + virtual sal_Bool SAL_CALL hasQuery() override + { return base_.hasQuery(); } + + virtual OUString SAL_CALL getQuery() override + { return base_.getQuery(); } + + virtual sal_Bool SAL_CALL hasFragment() override + { return base_.hasFragment(); } + + virtual OUString SAL_CALL getFragment() override + { return base_.getFragment(); } + + virtual void SAL_CALL setFragment(OUString const & fragment) override + { base_.setFragment(fragment); } + + virtual void SAL_CALL clearFragment() override + { base_.clearFragment(); } + + virtual OUString SAL_CALL expand( + css::uno::Reference< css::util::XMacroExpander > const & expander) override; + +private: + virtual ~UrlReference() override {} + + stoc::uriproc::UriReference base_; +}; + +OUString UrlReference::expand( + css::uno::Reference< css::util::XMacroExpander > const & expander) +{ + if (!expander.is()) { + throw css::uno::RuntimeException("null expander passed to XVndSunStarExpandUrl.expand"); + } + return expander->expandMacros( + ::rtl::Uri::decode( + getPath(), ::rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8)); +} + +class Parser: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XUriSchemeParser> +{ +public: + Parser() {} + + Parser(const Parser&) = delete; + Parser& operator=(const Parser&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService( + OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + parse( + OUString const & scheme, + OUString const & schemeSpecificPart) override; + +private: + virtual ~Parser() override {} +}; + +OUString Parser::getImplementationName() +{ + return "com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTexpand"; +} + +sal_Bool Parser::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Parser::getSupportedServiceNames() +{ + return { "com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTexpand" }; +} + +css::uno::Reference< css::uri::XUriReference > Parser::parse( + OUString const & scheme, OUString const & schemeSpecificPart) +{ + if (!parseSchemeSpecificPart(schemeSpecificPart)) { + return css::uno::Reference< css::uri::XUriReference >(); + } + return new UrlReference(scheme, schemeSpecificPart); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTexpand_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + //TODO: single instance + return ::cppu::acquire(new Parser()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx new file mode 100644 index 0000000000..1f53351693 --- /dev/null +++ b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "UriReference.hxx" + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/XUriSchemeParser.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/character.hxx> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <o3tl/safeint.hxx> + +#include <string_view> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { class XInterface; } +namespace com::sun::star::uri { class XUriReference; } + +namespace { + +int getHexWeight(sal_Unicode c) { + return c >= '0' && c <= '9' ? static_cast< int >(c - '0') + : c >= 'A' && c <= 'F' ? static_cast< int >(c - 'A' + 10) + : c >= 'a' && c <= 'f' ? static_cast< int >(c - 'a' + 10) + : -1; +} + +int parseEscaped(std::u16string_view part, sal_Int32 * index) { + if (part.size() - *index < 3 || part[*index] != '%') { + return -1; + } + int n1 = getHexWeight(part[*index + 1]); + int n2 = getHexWeight(part[*index + 2]); + if (n1 < 0 || n2 < 0) { + return -1; + } + *index += 3; + return (n1 << 4) | n2; +} + +OUString parsePart( + std::u16string_view part, bool namePart, sal_Int32 * index) +{ + OUStringBuffer buf(64); + while (o3tl::make_unsigned(*index) < part.size()) { + sal_Unicode c = part[*index]; + if (namePart ? c == '?' : c == '&' || c == '=') { + break; + } else if (c == '%') { + sal_Int32 i = *index; + int n = parseEscaped(part, &i); + if (n >= 0 && n <= 0x7F) { + buf.append(static_cast< sal_Unicode >(n)); + } else if (n >= 0xC0 && n <= 0xFC) { + sal_Int32 encoded; + int shift; + sal_Int32 min; + if (n <= 0xDF) { + encoded = (n & 0x1F) << 6; + shift = 0; + min = 0x80; + } else if (n <= 0xEF) { + encoded = (n & 0x0F) << 12; + shift = 6; + min = 0x800; + } else if (n <= 0xF7) { + encoded = (n & 0x07) << 18; + shift = 12; + min = 0x10000; + } else if (n <= 0xFB) { + encoded = (n & 0x03) << 24; + shift = 18; + min = 0x200000; + } else { + encoded = 0; + shift = 24; + min = 0x4000000; + } + bool utf8 = true; + for (; shift >= 0; shift -= 6) { + n = parseEscaped(part, &i); + if (n < 0x80 || n > 0xBF) { + utf8 = false; + break; + } + encoded |= (n & 0x3F) << shift; + } + if (!utf8 || !rtl::isUnicodeScalarValue(encoded) + || encoded < min) + { + break; + } + buf.appendUtf32(encoded); + } else { + break; + } + *index = i; + } else { + buf.append(c); + ++*index; + } + } + return buf.makeStringAndClear(); +} + +OUString encodeNameOrParamFragment(OUString const & fragment) { + static constexpr auto nameOrParamFragment = rtl::createUriCharClass( + u8"!$'()*+,-.0123456789:;@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~"); + return rtl::Uri::encode( + fragment, nameOrParamFragment.data(), rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8); +} + +bool parseSchemeSpecificPart(std::u16string_view part) { + size_t len = part.size(); + sal_Int32 i = 0; + if (parsePart(part, true, &i).isEmpty() || part[0] == '/') { + return false; + } + if (o3tl::make_unsigned(i) == len) { + return true; + } + for (;;) { + ++i; // skip '?' or '&' + if (parsePart(part, false, &i).isEmpty() || o3tl::make_unsigned(i) == len + || part[i] != '=') + { + return false; + } + ++i; + parsePart(part, false, &i); + if (o3tl::make_unsigned(i) == len) { + return true; + } + if (part[i] != '&') { + return false; + } + } +} + +class UrlReference: + public cppu::WeakImplHelper<css::uri::XVndSunStarScriptUrlReference> +{ +public: + UrlReference(OUString const & scheme, OUString const & path): + m_base( + scheme, false, OUString(), path, false, OUString()) + {} + + UrlReference(const UrlReference&) = delete; + UrlReference& operator=(const UrlReference&) = delete; + + virtual OUString SAL_CALL getUriReference() override + { return m_base.getUriReference(); } + + virtual sal_Bool SAL_CALL isAbsolute() override + { return m_base.isAbsolute(); } + + virtual OUString SAL_CALL getScheme() override + { return m_base.getScheme(); } + + virtual OUString SAL_CALL getSchemeSpecificPart() override + { return m_base.getSchemeSpecificPart(); } + + virtual sal_Bool SAL_CALL isHierarchical() override + { return m_base.isHierarchical(); } + + virtual sal_Bool SAL_CALL hasAuthority() override + { return m_base.hasAuthority(); } + + virtual OUString SAL_CALL getAuthority() override + { return m_base.getAuthority(); } + + virtual OUString SAL_CALL getPath() override + { return m_base.getPath(); } + + virtual sal_Bool SAL_CALL hasRelativePath() override + { return m_base.hasRelativePath(); } + + virtual sal_Int32 SAL_CALL getPathSegmentCount() override + { return m_base.getPathSegmentCount(); } + + virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override + { return m_base.getPathSegment(index); } + + virtual sal_Bool SAL_CALL hasQuery() override + { return m_base.hasQuery(); } + + virtual OUString SAL_CALL getQuery() override + { return m_base.getQuery(); } + + virtual sal_Bool SAL_CALL hasFragment() override + { return m_base.hasFragment(); } + + virtual OUString SAL_CALL getFragment() override + { return m_base.getFragment(); } + + virtual void SAL_CALL setFragment(OUString const & fragment) override + { m_base.setFragment(fragment); } + + virtual void SAL_CALL clearFragment() override + { m_base.clearFragment(); } + + virtual OUString SAL_CALL getName() override; + + virtual void SAL_CALL setName(OUString const & name) override; + + virtual sal_Bool SAL_CALL hasParameter(OUString const & key) override; + + virtual OUString SAL_CALL getParameter(OUString const & key) override; + + virtual void SAL_CALL setParameter(OUString const & key, OUString const & value) override; + +private: + virtual ~UrlReference() override {} + + sal_Int32 findParameter(std::u16string_view key) const; + + stoc::uriproc::UriReference m_base; +}; + +OUString UrlReference::getName() { + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = 0; + return parsePart(m_base.m_path, true, &i); +} + +void SAL_CALL UrlReference::setName(OUString const & name) +{ + if (name.isEmpty()) + throw css::lang::IllegalArgumentException( + OUString(), *this, 1); + + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = 0; + parsePart(m_base.m_path, true, &i); + + m_base.m_path = encodeNameOrParamFragment(name) + m_base.m_path.subView(i); +} + +sal_Bool UrlReference::hasParameter(OUString const & key) +{ + std::lock_guard g(m_base.m_mutex); + return findParameter(key) >= 0; +} + +OUString UrlReference::getParameter(OUString const & key) +{ + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = findParameter(key); + return i >= 0 ? parsePart(m_base.m_path, false, &i) : OUString(); +} + +void UrlReference::setParameter(OUString const & key, OUString const & value) +{ + if (key.isEmpty()) + throw css::lang::IllegalArgumentException( + OUString(), *this, 1); + + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = findParameter(key); + bool bExistent = ( i>=0 ); + if (!bExistent) { + i = m_base.m_path.getLength(); + } + + OUStringBuffer newPath(128); + newPath.append(m_base.m_path.subView(0, i)); + if (!bExistent) { + newPath.append( m_base.m_path.indexOf('?') < 0 ? '?' : '&' ); + newPath.append(encodeNameOrParamFragment(key) + "="); + } + newPath.append(encodeNameOrParamFragment(value)); + if (bExistent) { + /*oldValue = */ + parsePart(m_base.m_path, false, &i); // skip key + newPath.append(m_base.m_path.subView(i)); + } + + m_base.m_path = newPath.makeStringAndClear(); +} + +sal_Int32 UrlReference::findParameter(std::u16string_view key) const { + sal_Int32 i = 0; + parsePart(m_base.m_path, true, &i); // skip name + for (;;) { + if (i == m_base.m_path.getLength()) { + return -1; + } + ++i; // skip '?' or '&' + OUString k = parsePart(m_base.m_path, false, &i); + ++i; // skip '=' + if (k == key) { + return i; + } + parsePart(m_base.m_path, false, &i); // skip value + } +} + +class Parser: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XUriSchemeParser> +{ +public: + Parser() {} + + Parser(const Parser&) = delete; + Parser& operator=(const Parser&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + parse( + OUString const & scheme, OUString const & schemeSpecificPart) override; + +private: + virtual ~Parser() override {} +}; + +OUString Parser::getImplementationName() +{ + return "com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"; +} + +sal_Bool Parser::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Parser::getSupportedServiceNames() +{ + return { "com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript" }; +} + +css::uno::Reference< css::uri::XUriReference > +Parser::parse( + OUString const & scheme, OUString const & schemeSpecificPart) +{ + if (!parseSchemeSpecificPart(schemeSpecificPart)) { + return nullptr; + } + return new UrlReference(scheme, schemeSpecificPart); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTscript_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + //TODO: single instance + return ::cppu::acquire(new Parser()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory.cxx b/stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory.cxx new file mode 100644 index 0000000000..7520c460f1 --- /dev/null +++ b/stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory.cxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/textenc.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustring.hxx> +#include <utility> +#include <sal/types.h> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { class XInterface; } + +namespace { + +class Factory: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XVndSunStarPkgUrlReferenceFactory> +{ +public: + explicit Factory( + css::uno::Reference< css::uno::XComponentContext > context): + m_context(std::move(context)) {} + + Factory(const Factory&) = delete; + Factory& operator=(const Factory&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + createVndSunStarPkgUrlReference( + css::uno::Reference< css::uri::XUriReference > const & authority) override; + +private: + virtual ~Factory() override {} + + css::uno::Reference< css::uno::XComponentContext > m_context; +}; + +OUString Factory::getImplementationName() +{ + return "com.sun.star.comp.uri.VndSunStarPkgUrlReferenceFactory"; +} + +sal_Bool Factory::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Factory::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > s { "com.sun.star.uri.VndSunStarPkgUrlReferenceFactory" }; + return s; +} + +css::uno::Reference< css::uri::XUriReference > +Factory::createVndSunStarPkgUrlReference( + css::uno::Reference< css::uri::XUriReference > const & authority) +{ + if (!authority.is()) { + throw css::uno::RuntimeException( + "null authority passed to" + " XVndSunStarPkgUrlReferenceFactory.createVndSunStarPkgUrlReference"); + } + if (authority->isAbsolute() && !authority->hasFragment()) { + OUString buf = + "vnd.sun.star.pkg://" + + rtl::Uri::encode( + authority->getUriReference(), rtl_UriCharClassRegName, + rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8); + css::uno::Reference< css::uri::XUriReference > uriRef( + css::uri::UriReferenceFactory::create(m_context)->parse( + buf)); + return uriRef; + } else { + return css::uno::Reference< css::uri::XUriReference >(); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_VndSunStarPkgUrlReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext, + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new Factory(rxContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/dump.cxx b/stoc/test/dump.cxx new file mode 100644 index 0000000000..798e05acc2 --- /dev/null +++ b/stoc/test/dump.cxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 <sal/config.h> + +#include <com/sun/star/beans/PropertyChangeEvent.hpp> +#include <com/sun/star/reflection/Dump.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/TypeClass.hpp> +#include <cppuhelper/bootstrap.hxx> +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <test/codemaker/cppumaker/ByteBits.hpp> +#include <test/codemaker/cppumaker/Constants.hpp> +#include <test/codemaker/cppumaker/ShortBits.hpp> +#include <test/codemaker/cppumaker/UnsignedHyperBits.hpp> + +namespace +{ +class Dump : public CppUnit::TestFixture +{ +public: + void setUp() override + { + dump_ = css::reflection::Dump::get(cppu::defaultBootstrap_InitialComponentContext()); + } + + void testSequence() + { + CPPUNIT_ASSERT_EQUAL(OUString("[]"), + dump_->dumpValue(css::uno::Any(css::uno::Sequence<sal_Int32>{}))); + CPPUNIT_ASSERT_EQUAL(OUString("[1]"), + dump_->dumpValue(css::uno::Any(css::uno::Sequence<sal_Int32>{ 1 }))); + CPPUNIT_ASSERT_EQUAL(OUString("[1, 2, 3]"), dump_->dumpValue(css::uno::Any( + css::uno::Sequence<sal_Int32>{ 1, 2, 3 }))); + CPPUNIT_ASSERT_EQUAL(OUString("[[long: 1], [string: \"2\"], [[]long: [1, 2]]]"), + (dump_->dumpValue(css::uno::Any(css::uno::Sequence<css::uno::Any>{ + css::uno::Any(sal_Int32(1)), css::uno::Any(OUString("2")), + css::uno::Any(css::uno::Sequence<sal_Int32>{ 1, 2 }) })))); + } + + void testEnum() + { + CPPUNIT_ASSERT_EQUAL(OUString("ENUM"), + dump_->dumpValue(css::uno::Any(css::uno::TypeClass_ENUM))); + CPPUNIT_ASSERT_EQUAL(OUString("-1"), + dump_->dumpValue(css::uno::Any(css::uno::TypeClass(-1)))); + CPPUNIT_ASSERT_EQUAL(OUString("12345"), + dump_->dumpValue(css::uno::Any(css::uno::TypeClass(12345)))); + } + + void testStruct() + { + CPPUNIT_ASSERT_EQUAL( + OUString("[Source: null, PropertyName: \"test\", Further: false, PropertyHandle: 3, " + "OldValue: [void: void], NewValue: [long: 5]]"), + dump_->dumpValue(css::uno::Any(css::beans::PropertyChangeEvent( + {}, "test", false, 3, {}, css::uno::Any(sal_Int32(5)))))); + } + + void testConstantsGroup() + { + CPPUNIT_ASSERT_EQUAL(OUString("byteMin"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(sal_Int8(-128)))); + CPPUNIT_ASSERT_EQUAL(OUString("byteMax"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(sal_Int8(127)))); + CPPUNIT_ASSERT_EQUAL(OUString("longMin"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(sal_Int32(-2147483648)))); + CPPUNIT_ASSERT_EQUAL(OUString("longMax"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(sal_Int32(2147483647)))); + CPPUNIT_ASSERT_EQUAL(OUString("hyperMin"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(SAL_MIN_INT64))); + CPPUNIT_ASSERT_EQUAL(OUString("hyperMax"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(SAL_MAX_INT64))); + CPPUNIT_ASSERT_EQUAL(OUString("17"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(sal_Int32(17)))); + CPPUNIT_ASSERT_EQUAL(OUString("2147483646"), + dump_->dumpConstant("test.codemaker.cppumaker.Constants", + css::uno::Any(sal_Int32(2147483646)))); + + CPPUNIT_ASSERT_EQUAL(OUString("0"), dump_->dumpConstant("test.codemaker.cppumaker.ByteBits", + css::uno::Any(sal_Int8(0)))); + CPPUNIT_ASSERT_EQUAL( + OUString("BIT0+BIT2"), + dump_->dumpConstant("test.codemaker.cppumaker.ByteBits", css::uno::Any(sal_Int8(5)))); + CPPUNIT_ASSERT_EQUAL( + OUString("BIT4"), + dump_->dumpConstant("test.codemaker.cppumaker.ByteBits", css::uno::Any(sal_Int8(16)))); + CPPUNIT_ASSERT_EQUAL( + OUString("BIT0+BIT4"), + dump_->dumpConstant("test.codemaker.cppumaker.ByteBits", css::uno::Any(sal_Int8(17)))); + CPPUNIT_ASSERT_EQUAL(OUString("BIT7"), + dump_->dumpConstant("test.codemaker.cppumaker.ByteBits", + css::uno::Any(sal_Int8(-128)))); + CPPUNIT_ASSERT_EQUAL( + OUString("ALL"), + dump_->dumpConstant("test.codemaker.cppumaker.ByteBits", css::uno::Any(sal_Int8(-1)))); + + CPPUNIT_ASSERT_EQUAL(OUString("BIT7"), + dump_->dumpConstant("test.codemaker.cppumaker.ShortBits", + css::uno::Any(sal_Int16(128)))); + CPPUNIT_ASSERT_EQUAL(OUString("ALL"), + dump_->dumpConstant("test.codemaker.cppumaker.ShortBits", + css::uno::Any(sal_Int16(-1)))); + + CPPUNIT_ASSERT_EQUAL(OUString("BIT63"), + dump_->dumpConstant("test.codemaker.cppumaker.UnsignedHyperBits", + css::uno::Any(sal_uInt64(9223372036854775808u)))); + CPPUNIT_ASSERT_EQUAL(OUString("BIT0+BIT62"), + dump_->dumpConstant("test.codemaker.cppumaker.UnsignedHyperBits", + css::uno::Any(sal_uInt64(4611686018427387905)))); + CPPUNIT_ASSERT_EQUAL(OUString("BIT0+BIT63"), + dump_->dumpConstant("test.codemaker.cppumaker.UnsignedHyperBits", + css::uno::Any(sal_uInt64(9223372036854775809u)))); + CPPUNIT_ASSERT_EQUAL(OUString("ALL"), + dump_->dumpConstant("test.codemaker.cppumaker.UnsignedHyperBits", + css::uno::Any(SAL_MAX_UINT64))); + } + + CPPUNIT_TEST_SUITE(Dump); + CPPUNIT_TEST(testSequence); + CPPUNIT_TEST(testEnum); + CPPUNIT_TEST(testStruct); + CPPUNIT_TEST(testConstantsGroup); + CPPUNIT_TEST_SUITE_END(); + +private: + css::uno::Reference<css::reflection::XDump> dump_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(Dump); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/stoc/test/javavm/jvm_interaction/interactionhandler.cxx b/stoc/test/javavm/jvm_interaction/interactionhandler.cxx new file mode 100644 index 0000000000..f1c4baf863 --- /dev/null +++ b/stoc/test/javavm/jvm_interaction/interactionhandler.cxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <jni.h> + +#include <stdio.h> +#include <sal/main.h> +#include <rtl/process.h> + +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/bootstrap.hxx> +#include <osl/thread.h> + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/java/XJavaVM.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/java/XJavaThreadRegister_11.hpp> + +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/task/XInteractionContinuation.hpp> +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionRetry.hpp> +#include <com/sun/star/java/JavaNotConfiguredException.hpp> +#include <com/sun/star/java/MissingJavaRuntimeException.hpp> +#include <com/sun/star/java/JavaDisabledException.hpp> +#include <com/sun/star/java/JavaVMCreationFailureException.hpp> +#include <cppuhelper/implbase.hxx> +#include <uno/current_context.hxx> + +using namespace cppu; +using namespace css::uno; +using namespace css::lang; +using namespace css::registry; +using namespace css::java; +using namespace css::task; + + +#define INTERACTION_HANDLER_NAME "java-vm.interaction-handler" + +class Context: public WeakImplHelper<XCurrentContext> +{ + virtual Any SAL_CALL getValueByName( const OUString& Name ) throw (RuntimeException); +}; + +class InteractionHandler: public WeakImplHelper<XInteractionHandler> +{ + virtual void SAL_CALL handle( const Reference< XInteractionRequest >& Request ) + throw (RuntimeException); +}; + +Any SAL_CALL Context::getValueByName( const OUString& Name) throw (RuntimeException) +{ + Any retVal; + if( Name.equals( INTERACTION_HANDLER_NAME)) + { + Reference<XInteractionHandler> handler(new InteractionHandler()); + retVal <<= handler; + } + return retVal; +} + +void SAL_CALL InteractionHandler::handle( const Reference< XInteractionRequest >& Request ) + throw (RuntimeException) +{ + Any anyExc= Request->getRequest(); + Sequence<Reference< XInteractionContinuation> >seqCont= Request->getContinuations(); + + Reference<XInteractionAbort> abort; + Reference<XInteractionRetry> retry; + + for (sal_Int32 i= 0; i < seqCont.getLength(); i++) + { + abort.set( seqCont[i], UNO_QUERY ); + if(abort.is()) + break; + } + for (sal_Int32 i= 0; i < seqCont.getLength(); i++) + { + retry.set( seqCont[i], UNO_QUERY ); + if(retry.is()) + break; + } + + static int cRetry= 0; + + if( cRetry++ == 5) + { + if( abort.is()) + abort->select(); + return; + } + if( retry.is()) + retry->select(); +} + +sal_Bool test1(const Reference< XMultiServiceFactory > & xMgr ) +{ + sal_Bool retVal= sal_True; + setCurrentContext(Reference<XCurrentContext>(new Context()); + + OUString sVMService("com.sun.star.java.JavaVirtualMachine"); + Reference<XInterface> xXInt= xMgr->createInstance(sVMService); + if( ! xXInt.is()) + return sal_False; + Reference<XJavaVM> xVM( xXInt, UNO_QUERY); + if( ! xVM.is()) + return sal_False; + + + sal_Int8 arId[16]; + rtl_getGlobalProcessId((sal_uInt8*) arId); + + Any anyVM; + try + { + anyVM = xVM->getJavaVM( Sequence<sal_Int8>(arId, 16)); + } + catch (const JavaNotConfiguredException& e) + { + OString msg= OUStringToOString(e.Message, osl_getThreadTextEncoding()); + printf("JavaNotConfiguredException: %s\n", msg.getStr()); + } + catch (const JavaVMCreationFailureException& e) + { + OString msg= OUStringToOString(e.Message, osl_getThreadTextEncoding()); + printf("JavaVMCreationFailureException: %s\n", msg.getStr()); + } + catch (const MissingJavaRuntimeException& e) + { + OString msg= OUStringToOString(e.Message, osl_getThreadTextEncoding()); + printf("MissingJavaRuntimeException: %s\n", msg.getStr()); + } + catch (const JavaDisabledException& e) + { + OString msg= OUStringToOString(e.Message, osl_getThreadTextEncoding()); + printf("JavaDisabledException: %s\n", msg.getStr()); + } + catch (const RuntimeException & e) + { + OString msg= OUStringToOString(e.Message, osl_getThreadTextEncoding()); + printf("###RuntimeException: %s\n", msg.getStr()); + retVal= sal_False; + } + return retVal; +} + +SAL_IMPLEMENT_MAIN() +{ + Reference<XSimpleRegistry> xreg= createSimpleRegistry(); + xreg->open( OUString("applicat.rdb"), + sal_False, sal_False ); + + Reference< XComponentContext > context= bootstrap_InitialComponentContext(xreg); + Reference<XMultiComponentFactory> fac= context->getServiceManager(); + Reference<XMultiServiceFactory> xMgr( fac, UNO_QUERY); + + sal_Bool bSucc = test1(xMgr); + Reference< XComponent > xCompContext( context, UNO_QUERY ); + xCompContext->dispose(); + return (bSucc ? 0 : -1); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/javavm/jvm_interaction/makefile.mk b/stoc/test/javavm/jvm_interaction/makefile.mk new file mode 100644 index 0000000000..c9c1084a52 --- /dev/null +++ b/stoc/test/javavm/jvm_interaction/makefile.mk @@ -0,0 +1,71 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +PRJ=..$/..$/.. +TARGET= jvminteraction +PRJNAME= stoc +TARGET6= $(TARGET) +TARGETTYPE= CUI +LIBTARGET= NO +ENABLE_EXCEPTIONS=TRUE + +UNOUCRDEP= $(SOLARBINDIR)$/udkapi.rdb +UNOUCRRDB= $(SOLARBINDIR)$/udkapi.rdb +UNOUCROUT= $(OUT)$/inc$ +INCPRE+= $(OUT)$/inc$ + + + +# --- Settings ----------------------------------------------------- +.INCLUDE : settings.mk + +# --- Application 6 - testjavavm ------------------------------------ + +UNOTYPES= com.sun.star.lang.XMultiComponentFactory \ + com.sun.star.uno.XWeak \ + com.sun.star.java.XJavaVM \ + com.sun.star.java.XJavaThreadRegister_11 \ + com.sun.star.java.JavaNotConfiguredException \ + com.sun.star.java.MissingJavaRuntimeException \ + com.sun.star.java.JavaDisabledException \ + com.sun.star.java.JavaVMCreationFailureException \ + com.sun.star.registry.XSimpleRegistry \ + com.sun.star.lang.XComponent \ + com.sun.star.registry.XImplementationRegistration \ + com.sun.star.lang.XSingleServiceFactory \ + com.sun.star.uno.TypeClass \ + com.sun.star.lang.XMultiServiceFactory \ + com.sun.star.uno.XCurrentContext \ + com.sun.star.task.XInteractionHandler \ + com.sun.star.task.XInteractionRequest \ + com.sun.star.task.XInteractionContinuation \ + com.sun.star.task.XInteractionAbort \ + com.sun.star.task.XInteractionRetry \ + + +APP6TARGET= $(TARGET6) +APP6OBJS = $(OBJ)$/interactionhandler.obj +APP6STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(SALLIB) + +# --- Target ------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/stoc/test/javavm/makefile.mk b/stoc/test/javavm/makefile.mk new file mode 100644 index 0000000000..eb0ffb7619 --- /dev/null +++ b/stoc/test/javavm/makefile.mk @@ -0,0 +1,59 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +PRJ=..$/.. +TARGET= testjavavm +PRJNAME= stoc +TARGET6= testjavavm +TARGETTYPE= CUI +LIBTARGET= NO +ENABLE_EXCEPTIONS=TRUE + +UNOUCRDEP= $(SOLARBINDIR)$/udkapi.rdb +UNOUCRRDB= $(SOLARBINDIR)$/udkapi.rdb +UNOUCROUT= $(OUT)$/inc$ +INCPRE+= $(OUT)$/inc$ + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Application 6 - testjavavm ------------------------------------ + +UNOTYPES= com.sun.star.lang.XMultiComponentFactory \ + com.sun.star.uno.XWeak \ + com.sun.star.java.XJavaVM \ + com.sun.star.java.XJavaThreadRegister_11 \ + com.sun.star.registry.XSimpleRegistry \ + com.sun.star.lang.XComponent \ + com.sun.star.registry.XImplementationRegistration \ + com.sun.star.lang.XSingleServiceFactory \ + com.sun.star.uno.TypeClass \ + com.sun.star.lang.XMultiServiceFactory + +APP6TARGET= $(TARGET6) +APP6OBJS = $(OBJ)$/testjavavm.obj +APP6STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(SALLIB) + +# --- Target ------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/stoc/test/javavm/testapplet/TestApplet.html b/stoc/test/javavm/testapplet/TestApplet.html new file mode 100644 index 0000000000..e83d2ffe7d --- /dev/null +++ b/stoc/test/javavm/testapplet/TestApplet.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"> +<HTML> +<HEAD> + <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> + <TITLE>A Clock (1.1)</TITLE> + <META NAME="GENERATOR" CONTENT="StarOffice 6.0 (Win32)"> + <META NAME="CREATED" CONTENT="20011119;12262427"> + <META NAME="CHANGED" CONTENT="16010101;0"> +</HEAD> +<BODY LANG="de-DE"> +<H1>TestApplet</H1> +<HR> +<P> +<APPLET CODE="TestApplet.class" ALIGN=BOTTOM WIDTH=170 HEIGHT=150> +</APPLET> +</P> + +</BODY> +</HTML>
\ No newline at end of file diff --git a/stoc/test/javavm/testapplet/TestApplet.java b/stoc/test/javavm/testapplet/TestApplet.java new file mode 100644 index 0000000000..e271f4dd1f --- /dev/null +++ b/stoc/test/javavm/testapplet/TestApplet.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import java.awt.Color; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; + +public class TestApplet extends java.applet.Applet { + + /** Initialization method that will be called after the applet is loaded + * into the browser. + */ + @Override + public void init () { + setBackground( Color.green); + resize( 300, 300); + + // Security tests. + File f= new File("d:\\temp\\javasecurity.txt"); + try { + f.createNewFile(); + + // local connection + URL url= new URL("http://localhost:8080/index.html"); + url.openStream(); + // remote connection + url= new URL("http://www.w3.org/index.html"); + url.openStream(); + }catch( MalformedURLException mue) { + }catch( IOException e) { + String s= e.getMessage(); + System.out.println(s); + } + + } +} diff --git a/stoc/test/javavm/testapplet/makefile.mk b/stoc/test/javavm/testapplet/makefile.mk new file mode 100644 index 0000000000..c2b5a2001f --- /dev/null +++ b/stoc/test/javavm/testapplet/makefile.mk @@ -0,0 +1,43 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ=..$/..$/.. + +PRJNAME = testapplet +PACKAGE = +TARGET = TestApplet + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# Files -------------------------------------------------------- + +JARFILES = ridl.jar jurt.jar unoil.jar + +JAVAFILES= $(subst,$(CLASSDIR)$/$(PACKAGE)$/, $(subst,.class,.java $(JAVACLASSFILES))) + + +JAVACLASSFILES= \ + $(CLASSDIR)$/$(PACKAGE)$/TestApplet.class + +JARCLASSDIRS= . + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/stoc/test/javavm/testcomponent/TestComponent.java b/stoc/test/javavm/testcomponent/TestComponent.java new file mode 100644 index 0000000000..d976323c5f --- /dev/null +++ b/stoc/test/javavm/testcomponent/TestComponent.java @@ -0,0 +1,129 @@ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +import com.sun.star.comp.loader.FactoryHelper; +import com.sun.star.lang.*; +import com.sun.star.uno.*; +import com.sun.star.registry.XRegistryKey; +import java.io.*; +import java.net.*; + + +/** This component implements XTypeProvider for use with StarBasic. + * The XServiceInfo is implemented to have an interface in which we can put some + * code just for the sake of debugging. + * + * To debug with JPDA (jdk 1.3), put these lines in the java.ini within the [Java] section: + * -Xdebug + * -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y + * + * When the Virtual Machine service is instantiated it will block until the debugger + * attaches to it on port 8000. You can chose a different port. You attach to the VM using + * jdb by + * + * jdb -connect com.sun.jdi.SocketAttach:hostname=myhost,port=8000 + * + * myhost is the hostname where the VM is running. +*/ +public class TestComponent implements XServiceInfo, XTypeProvider +{ + public static final String __serviceName="JavaTestComponent"; + + // XTypeProvider + public com.sun.star.uno.Type[] getTypes( ) + { + Type[] retValue= new Type[2]; + retValue[0]= new Type( XServiceInfo.class); + retValue[1]= new Type( XTypeProvider.class); + return retValue; + } + // XTypeProvider + public byte[] getImplementationId( ) + { + return new byte[0]; + } + + + // XServiceName + public String getImplementationName( ) + { + // the functions are for debugging + + // Test security settings + File f= new File("c:/temp/javasecurity.txt"); + try { + f.createNewFile(); + + // local connection + URL url= new URL("http://localhost:8080/index.html"); + url.openStream(); + // remote connection + url= new URL("http://www.w3.org/index.html"); + url.openStream(); + }catch( MalformedURLException mue) { + }catch( IOException e) { + String s= e.getMessage(); + System.out.println(s); + } + + return __serviceName; + } + // XServiceName + public boolean supportsService( /*IN*/String ServiceName ) + { + + return false; + } + + //XServiceName + public String[] getSupportedServiceNames( ) + { + String[] retValue= new String[0]; + return retValue; + } + + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, + XRegistryKey regKey) + { + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals( TestComponent.class.getName()) ) + xSingleServiceFactory = FactoryHelper.getServiceFactory( TestComponent.class, + TestComponent.__serviceName, + multiFactory, + regKey); + + return xSingleServiceFactory; + } + + /** + * Writes the service information into the given registry key. + * This method is called by the <code>JavaLoader</code> + * <p> + * @return returns true if the operation succeeded + * @param regKey the registryKey + * @see com.sun.star.comp.loader.JavaLoader + */ + public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) + { + return FactoryHelper.writeRegistryServiceInfo( TestComponent.class.getName(), + TestComponent.__serviceName, regKey); + } + +} diff --git a/stoc/test/javavm/testcomponent/makefile.mk b/stoc/test/javavm/testcomponent/makefile.mk new file mode 100644 index 0000000000..89ae042adc --- /dev/null +++ b/stoc/test/javavm/testcomponent/makefile.mk @@ -0,0 +1,45 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ=..$/..$/.. + +PRJNAME = testcomponent +PACKAGE = +TARGET = JavaTestComponent + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# Files -------------------------------------------------------- + +JARFILES = ridl.jar jurt.jar unoil.jar + +CUSTOMMANIFESTFILE= manifest + +JARTARGET = $(TARGET).jar + +JAVAFILES= \ + TestComponent.java + + +JAVACLASSFILES = $(foreach,i,$(JAVAFILES) $(CLASSDIR)$/$(PACKAGE)$/$(i:b).class) + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/stoc/test/javavm/testcomponent/manifest b/stoc/test/javavm/testcomponent/manifest new file mode 100644 index 0000000000..1763d7bb95 --- /dev/null +++ b/stoc/test/javavm/testcomponent/manifest @@ -0,0 +1 @@ +RegistrationClassName: TestComponent diff --git a/stoc/test/javavm/testjavavm.cxx b/stoc/test/javavm/testjavavm.cxx new file mode 100644 index 0000000000..0e243f62d3 --- /dev/null +++ b/stoc/test/javavm/testjavavm.cxx @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <jni.h> + +#include <stdio.h> +#include <sal/main.h> +#include <sal/log.hxx> +#include <rtl/process.h> + +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/bootstrap.hxx> + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/java/XJavaVM.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/java/XJavaThreadRegister_11.hpp> + +using namespace cppu; +using namespace css::uno; +using namespace css::lang; +using namespace css::registry; +using namespace css::java; + + +sal_Bool testJavaVM(const Reference< XMultiServiceFactory > & xMgr ) +{ + + OUString sVMService("com.sun.star.java.JavaVirtualMachine"); + Reference<XInterface> xXInt= xMgr->createInstance(sVMService); + if( ! xXInt.is()) + return sal_False; + Reference<XJavaVM> xVM( xXInt, UNO_QUERY); + if( ! xVM.is()) + return sal_False; + Reference<XJavaThreadRegister_11> xreg11(xVM, UNO_QUERY); + if( ! xreg11.is()) + return sal_False; + + + sal_Int8 arId[16]; + rtl_getGlobalProcessId((sal_uInt8*) arId); + Any anyVM = xVM->getJavaVM( Sequence<sal_Int8>(arId, 16)); + if ( ! anyVM.hasValue()) + { + OSL_FAIL("could not get Java VM"); + return sal_False; + } + + sal_Bool b= xreg11->isThreadAttached(); + xreg11->registerThread(); + b= xreg11->isThreadAttached(); + xreg11->revokeThread(); + b= xreg11->isThreadAttached(); + + + b= xVM->isVMEnabled(); + b= xVM->isVMStarted(); + + + b= xVM->isVMEnabled(); + b= xVM->isVMStarted(); + + + JavaVM* _jvm= *(JavaVM**) anyVM.getValue(); + JNIEnv *p_env; + if( _jvm->AttachCurrentThread((void**) &p_env, 0)) + return sal_False; + + jclass cls = p_env->FindClass( "TestJavaVM"); + if (cls == 0) { + SAL_WARN("stoc", "Can't find Prog class"); + exit(1); + } + + jmethodID id = p_env->GetStaticMethodID( cls, "getInt", "()I"); + if( id) + { + p_env->CallStaticIntMethod(cls, id); + } + + if( p_env->ExceptionOccurred()){ + p_env->ExceptionDescribe(); + p_env->ExceptionClear(); + } + + + _jvm->DetachCurrentThread(); + return sal_True; +} + +SAL_IMPLEMENT_MAIN() +{ + Reference<XSimpleRegistry> xreg= createSimpleRegistry(); + xreg->open( OUString("applicat.rdb"), + sal_False, sal_False ); + + Reference< XComponentContext > context= bootstrap_InitialComponentContext(xreg); + Reference<XMultiComponentFactory> fac= context->getServiceManager(); + Reference<XMultiServiceFactory> xMgr( fac, UNO_QUERY); + + sal_Bool bSucc = sal_False; + try + { + OUString sImplReg( + "com.sun.star.registry.ImplementationRegistration"); + Reference<css::registry::XImplementationRegistration> xImplReg( + xMgr->createInstance( sImplReg ), UNO_QUERY ); + OSL_ENSURE( xImplReg.is(), "### no impl reg!" ); + + + OUString sLibLoader("com.sun.star.loader.SharedLibrary"); + OUString sJenLib( "javavm.uno" SAL_DLLEXTENSION ); + xImplReg->registerImplementation( + sLibLoader, sJenLib, Reference< XSimpleRegistry >() ); + + bSucc = testJavaVM( xMgr ); + } + catch (const Exception & rExc) + { + DBG_UNHANDLED_EXCEPTION("stoc", "### exception occurred: " << rExc ); + } + + Reference< XComponent > xCompContext( context, UNO_QUERY ); + xCompContext->dispose(); + printf("javavm %s", bSucc ? "succeeded" : "failed"); + return (bSucc ? 0 : -1); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/javavm/testjavavm.java b/stoc/test/javavm/testjavavm.java new file mode 100644 index 0000000000..9309843b08 --- /dev/null +++ b/stoc/test/javavm/testjavavm.java @@ -0,0 +1,30 @@ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +class TestJavaVM +{ + public static int getInt() + { + return 100; + } + + public static void main( String args[]) + { + System.out.println("Hello World"); + } +} diff --git a/stoc/test/language_binding.idl b/stoc/test/language_binding.idl new file mode 100644 index 0000000000..db568f077d --- /dev/null +++ b/stoc/test/language_binding.idl @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test +{ + +enum TestEnum +{ + TEST, + ONE, + TWO, + CHECK, + LOLA, + PALOO, + ZA +}; + +/** + * simple c++ types + */ +struct TestSimple +{ + boolean Bool; + char Char; + byte Byte; + short Short; + unsigned short UShort; + long Long; + unsigned long ULong; + hyper Hyper; + unsigned hyper UHyper; + float Float; + double Double; + test::TestEnum Enum; +}; +/** + * complex c++ types + */ +struct TestElement : test::TestSimple +{ + string String; + com::sun::star::uno::XInterface Interface; + any Any; +}; +struct TestDataElements : test::TestElement +{ + sequence<test::TestElement > Sequence; +}; + +typedef TestDataElements TestData; + +/** Monster test interface to test language binding calls. */ +interface XLBTestBase : com::sun::star::uno::XInterface +{ + /** + * in parameter test, tests by calls reference also (complex types) + */ + void setValues( [in] boolean bBool, [in] char cChar, [in] byte nByte, + [in] short nShort, [in] unsigned short nUShort, + [in] long nLong, [in] unsigned long nULong, + [in] hyper nHyper, [in] unsigned hyper nUHyper, + [in] float fFloat, [in] double fDouble, + [in] test::TestEnum eEnum, [in] string aString, + [in] com::sun::star::uno::XInterface xInterface, [in] any aAny, + [in] sequence<test::TestElement > aSequence, + [in] test::TestData aStruct ); + /** + * inout parameter test + */ + test::TestData setValues2( [inout] boolean bBool, [inout] char cChar, [inout] byte nByte, + [inout] short nShort, [inout] unsigned short nUShort, + [inout] long nLong, [inout] unsigned long nULong, + [inout] hyper nHyper, [inout] unsigned hyper nUHyper, + [inout] float fFloat, [inout] double fDouble, + [inout] test::TestEnum eEnum, [inout] string aString, + [inout] com::sun::star::uno::XInterface xInterface, [inout] any aAny, + [inout] sequence<test::TestElement > aSequence, + [inout] test::TestData aStruct ); + + /** + * out parameter test + */ + test::TestData getValues( [out] boolean bBool, [out] char cChar, [out] byte nByte, + [out] short nShort, [out] unsigned short nUShort, + [out] long nLong, [out] unsigned long nULong, + [out] hyper nHyper, [out] unsigned hyper nUHyper, + [out] float fFloat, [out] double fDouble, + [out] test::TestEnum eEnum, [out] string aString, + [out] com::sun::star::uno::XInterface xInterface, [out] any aAny, + [out] sequence<test::TestElement > aSequence, + [out] test::TestData aStruct ); + + [attribute] boolean Bool; + [attribute] byte Byte; + [attribute] char Char; + [attribute] short Short; + [attribute] unsigned short UShort; + [attribute] long Long; + [attribute] unsigned long ULong; + [attribute] hyper Hyper; + [attribute] unsigned hyper UHyper; + [attribute] float Float; + [attribute] double Double; + [attribute] test::TestEnum Enum; + [attribute] string String; + [attribute] com::sun::star::uno::XInterface Interface; + [attribute] any Any; + [attribute] sequence<test::TestElement > Sequence; + [attribute] test::TestData Struct; +}; + + +/** Inheriting from monster; adds raiseException(). */ +interface XLanguageBindingTest : test::XLBTestBase +{ + /** + * params are there only for dummy, to test if all temp out params will be released. + */ + test::TestData raiseException( [out] boolean bBool, [out] char cChar, [out] byte nByte, + [out] short nShort, [out] unsigned short nUShort, + [out] long nLong, [out] unsigned long nULong, + [out] hyper nHyper, [out] unsigned hyper nUHyper, + [out] float fFloat, [out] double fDouble, + [out] test::TestEnum eEnum, [out] string aString, + [out] com::sun::star::uno::XInterface xInterface, [out] any aAny, + [out] sequence<test::TestElement > aSequence, + [out] test::TestData aStruct ) + raises( com::sun::star::lang::IllegalArgumentException ); + + /** + * raises runtime exception + */ + [attribute] long RuntimeException; +}; + +}; // test + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/makefile.mk b/stoc/test/makefile.mk new file mode 100644 index 0000000000..97c5950a35 --- /dev/null +++ b/stoc/test/makefile.mk @@ -0,0 +1,213 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ=.. + +PRJNAME=stoc +TARGET=stoc +TARGET1=testloader +TARGET2=testregistry +TARGET3=testsmgr +TARGET4=testcorefl +TARGET5=testinvocation +TARGET6=testintrosp +TARGET7=testconv +TARGET8=testproxyfac +TARGET9=testsmgr2 +TARGETTYPE=CUI + +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- smgr component ----------------------------------------------- +SHL1OBJS= \ + $(OBJ)$/testsmgr_cpnt.obj + +SHL1STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(SALLIB) + +SHL1TARGET=testsmgr_component +SHL1DEPN= +SHL1IMPLIB=i$(SHL1TARGET) +SHL1DEF=$(MISC)$/$(SHL1TARGET).def +DEF1NAME=$(SHL1TARGET) +SHL1VERSIONMAP=testsmgr_cpnt.map + +# --- Application 1 ------------------------------------------------ +APP1TARGET= $(TARGET1) +APP1OBJS= $(OBJ)$/testloader.obj + +APP1STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(SALLIB) + +# --- Application 2 ------------------------------------------------ +APP2TARGET= $(TARGET2) +APP2OBJS= $(OBJ)$/testregistry.obj $(OBJ)$/mergekeys_.obj + +APP2STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(REGLIB) \ + $(SALLIB) + +# --- Application 3 - testsmgr main ------------------------------------ +APP3TARGET= $(TARGET3) +APP3OBJS = $(OBJ)$/testsmgr.obj +APP3STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(REGLIB) \ + $(SALLIB) + +.IF "$(OS)"!="WNT" +APP3STDLIBS+= -l$(SHL1TARGET) +.ENDIF +.IF "$(OS)"=="WNT" +APP3STDLIBS+= i$(SHL1TARGET).lib +.ENDIF + +# --- Application 4 - testcorefl main ------------------------------------ +APP4TARGET= $(TARGET4) +APP4OBJS = $(OBJ)$/testcorefl.obj +APP4STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) + +# --- Application 5 - testinvocation main ------------------------------------ +APP5TARGET= $(TARGET5) +APP5OBJS = $(OBJ)$/testiadapter.obj +APP5STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) + +# --- Application 6 - testitrosp main ------------------------------------ +APP6TARGET= $(TARGET6) +APP6OBJS = $(OBJ)$/testintrosp.obj +APP6STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALHELPERLIB) \ + $(SALLIB) + +# --- Application 7 - testconv main ------------------------------------ +APP7TARGET= $(TARGET7) +APP7OBJS = $(OBJ)$/testconv.obj +APP7STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) + +# --- Application 8 - testproxyfac main ------------------------------------ +APP8TARGET= $(TARGET8) +APP8OBJS = $(OBJ)$/testproxyfac.obj +APP8STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) + +# --- Application 9 - testproxyfac main ------------------------------------ +APP9TARGET= $(TARGET9) +APP9OBJS = $(OBJ)$/testsmgr2.obj +APP9STDLIBS= \ + $(CPPULIB) \ + $(CPPUHELPERLIB) \ + $(SALLIB) + + +ALLIDLFILES:= testcorefl.idl language_binding.idl testintrosp.idl + + +# --- Target ------------------------------------------------ + +.IF "$(depend)" == "" +ALL : $(MISC)$/test_types_generated.flag \ + $(BIN)$/test1.rdb \ + $(BIN)$/test2.rdb \ + ALLTAR +.ELSE +ALL: ALLDEP +.ENDIF + +.INCLUDE : target.mk + +CPPUMAKERFLAGS = +.IF "$(COM)" == "MSC" +CPPUMAKERFLAGS = -L +.ENDIF + +FACTORYTYPES:= -T com.sun.star.lang.XSingleComponentFactory \ + -T com.sun.star.uno.XComponentContext \ + -T com.sun.star.uno.XWeak \ + -T com.sun.star.container.XEnumeration \ + -T com.sun.star.lang.XComponent \ + -T com.sun.star.registry.XSimpleRegistry \ + -T com.sun.star.lang.XInitialization \ + -T com.sun.star.lang.XMultiServiceFactory\ + -T com.sun.star.loader.XImplementationLoader \ + -T com.sun.star.registry.XImplementationRegistration \ + -T com.sun.star.container.XSet \ + -T com.sun.star.lang.XSingleServiceFactory\ + -T com.sun.star.lang.XServiceInfo \ + -T com.sun.star.container.XContentEnumerationAccess \ + -T com.sun.star.container.XEnumeration + +TESTCOREFL:=ModuleC;ModuleC.XInterfaceA;ModuleC.XInterfaceB;ModuleA.XInterface1;com.sun.star.reflection.XIdlReflection;com.sun.star.reflection.XIdlField;com.sun.star.reflection.XIdlArray;com.sun.star.reflection.XIdlMethod;com.sun.star.reflection.XIdlClass;com.sun.star.beans.XPropertySet;com.sun.star.lang.XComponent;com.sun.star.container.XHierarchicalNameAccess;com.sun.star.reflection.XIdlField2;com.sun.star.lang.DisposedException +TESTIADAPTER:=com.sun.star.beans.XIntrospection;com.sun.star.beans.MethodConcept;com.sun.star.beans.XExactName;com.sun.star.lang.XTypeProvider;com.sun.star.uno.XAggregation;com.sun.star.script.XInvocationAdapterFactory;com.sun.star.script.XInvocationAdapterFactory2;com.sun.star.script.XInvocation;com.sun.star.lang.XMultiServiceFactory;com.sun.star.registry.XSimpleRegistry;com.sun.star.lang.XInitialization;test.XLanguageBindingTest +TESTINTROSP:=ModuleA;ModuleA.XIntroTest;com.sun.star.beans.XPropertySet;com.sun.star.container.XIndexAccess;com.sun.star.container.XNameAccess;com.sun.star.beans.PropertyAttribute;com.sun.star.beans.PropertyConcept +TESTCONV:=com.sun.star.script.XTypeConverter +TESTPROXYFAC:=com.sun.star.reflection.XProxyFactory +TESTSECURITY:=com.sun.star.security.AllPermission;com.sun.star.security.XPolicy;com.sun.star.security.XAccessController;com.sun.star.io.FilePermission;com.sun.star.connection.SocketPermission;com.sun.star.uno.XCurrentContext + +$(BIN)$/test1.rdb: $(SHL1TARGETN) +.IF "$(OS)"!="WNT" + cp $(SHL1TARGETN) $(BIN) +.ENDIF + cd $(BIN) && regcomp -register -r test1.rdb -c $(SHL1TARGET) + +$(BIN)$/test2.rdb: + cd $(BIN) && regcomp -register -r test2.rdb -c remotebridge.uno$(DLLPOST) + +$(BIN)$/stoctest.rdb: $(ALLIDLFILES) + idlc -I$(PRJ) -I$(SOLARIDLDIR) -O$(BIN) $? + regmerge $@ /UCR $(BIN)$/{$(?:f:s/.idl/.urd/)} + regmerge $@ / $(SOLARBINDIR)$/udkapi.rdb + regcomp -register -r $@ -c reflection.uno$(DLLPOST) + touch $@ + +$(MISC)$/test_types_generated.flag : $(BIN)$/stoctest.rdb makefile.mk + -rm -f $(MISC)$/test_types_generated.flag + cppumaker $(CPPUMAKERFLAGS) -BUCR -O$(UNOUCROUT) $(FACTORYTYPES) -T"$(TESTIADAPTER)" $(BIN)$/stoctest.rdb + cppumaker $(CPPUMAKERFLAGS) -BUCR -O$(UNOUCROUT) -T"$(TESTCOREFL)" $(BIN)$/stoctest.rdb + cppumaker $(CPPUMAKERFLAGS) -BUCR -O$(UNOUCROUT) -T"$(TESTINTROSP)" $(BIN)$/stoctest.rdb + cppumaker $(CPPUMAKERFLAGS) -BUCR -O$(UNOUCROUT) -T"$(TESTCONV)" $(BIN)$/stoctest.rdb + cppumaker $(CPPUMAKERFLAGS) -BUCR -O$(UNOUCROUT) -T"$(TESTPROXYFAC)" $(BIN)$/stoctest.rdb + cppumaker $(CPPUMAKERFLAGS) -BUCR -O$(UNOUCROUT) -T"$(TESTSECURITY)" $(BIN)$/stoctest.rdb + touch $(MISC)$/test_types_generated.flag diff --git a/stoc/test/mergekeys_.cxx b/stoc/test/mergekeys_.cxx new file mode 100644 index 0000000000..079e12670a --- /dev/null +++ b/stoc/test/mergekeys_.cxx @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "../source/implementationregistration/mergekeys.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/registry_tdprovider/makefile.mk b/stoc/test/registry_tdprovider/makefile.mk new file mode 100644 index 0000000000..35a7851173 --- /dev/null +++ b/stoc/test/registry_tdprovider/makefile.mk @@ -0,0 +1,54 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ := ..$/.. +PRJNAME := stoc + +TARGET := test_registry_tdprovider + +ENABLE_EXCEPTIONS := TRUE + +.INCLUDE: settings.mk + +DLLPRE = # no leading "lib" on .so files + +SLOFILES = $(SLO)$/testregistrytdprovider.obj + +SHL1TARGET = $(ENFORCEDSHLPREFIX)testregistrytdprovider.uno +SHL1OBJS = $(SLOFILES) +SHL1VERSIONMAP = $(SOLARENV)/src/component.map +SHL1STDLIBS = $(CPPULIB) $(CPPUHELPERLIB) $(SALLIB) +SHL1IMPLIB = itestregistrytdprovider +DEF1NAME = $(SHL1TARGET) + +.INCLUDE: target.mk + +ALLTAR: test + +$(MISC)$/$(TARGET)$/all.rdb: types.idl + - rm $@ + - $(MKDIR) $(MISC)$/$(TARGET) + idlc -O$(MISC)$/$(TARGET) -I$(SOLARIDLDIR) -C -cid -we $< + regmerge $@ /UCR $(MISC)$/$(TARGET)$/types.urd + regmerge $@ / $(SOLARBINDIR)$/types.rdb + +$(SLOFILES): $(MISC)$/$(TARGET)$/all.rdb + +test .PHONY: $(SHL1TARGETN) $(MISC)$/$(TARGET)$/all.rdb + uno -c test.registrytdprovider.impl -l $(subst,$/,/ $(SHL1TARGETN)) \ + -ro $(subst,$/,/ $(MISC)$/$(TARGET)$/all.rdb) diff --git a/stoc/test/registry_tdprovider/readme.txt b/stoc/test/registry_tdprovider/readme.txt new file mode 100644 index 0000000000..9267df8582 --- /dev/null +++ b/stoc/test/registry_tdprovider/readme.txt @@ -0,0 +1,4 @@ +This test uses the delivered regtypeprov.uno dynamic library, not the local one. +(It might work to fix this, changing the test from a UNO component started from +the uno executable to a stand-alone application that bootstraps UNO in a special +way.) diff --git a/stoc/test/registry_tdprovider/testregistrytdprovider.cxx b/stoc/test/registry_tdprovider/testregistrytdprovider.cxx new file mode 100644 index 0000000000..46e3d0998b --- /dev/null +++ b/stoc/test/registry_tdprovider/testregistrytdprovider.cxx @@ -0,0 +1,798 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/lang/XMain.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/reflection/XCompoundTypeDescription.hpp> +#include <com/sun/star/reflection/XInterfaceAttributeTypeDescription2.hpp> +#include <com/sun/star/reflection/XInterfaceMemberTypeDescription.hpp> +#include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp> +#include <com/sun/star/reflection/XInterfaceTypeDescription2.hpp> +#include <com/sun/star/reflection/XPublished.hpp> +#include <com/sun/star/reflection/XServiceTypeDescription2.hpp> +#include <com/sun/star/reflection/XSingletonTypeDescription2.hpp> +#include <com/sun/star/reflection/XStructTypeDescription.hpp> +#include <com/sun/star/reflection/XTypeDescription.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/TypeClass.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/textenc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <uno/environment.h> + +#include /*MSVC trouble: <cstdlib>*/ <stdlib.h> +#include <iostream> +#include <ostream> + +namespace { + +class Service: public cppu::WeakImplHelper< css::lang::XMain > { +public: + virtual sal_Int32 SAL_CALL + run(css::uno::Sequence< OUString > const & arguments) + throw (css::uno::RuntimeException); + + static OUString getImplementationName(); + + static css::uno::Sequence< OUString > getSupportedServiceNames(); + + static css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( + css::uno::Reference< css::uno::XComponentContext > const & context) + throw (css::uno::Exception); + +private: + explicit Service( + css::uno::Reference< css::uno::XComponentContext > const & context): + m_context(context) + {} + + css::uno::Reference< css::uno::XComponentContext > m_context; +}; + +} + +namespace { + +std::ostream & operator <<(std::ostream & out, OUString const & value) { + return out << OUStringToOString(value, RTL_TEXTENCODING_UTF8).getStr(); +} + +void assertTrue(bool argument) { + if (!argument) { + std::cerr + << "assertTrue(" << argument << ") failed" << std::endl; + /*MSVC trouble: std::*/abort(); + } +} + +void assertFalse(bool argument) { + if (argument) { + std::cerr + << "assertFalse(" << argument << ") failed" << std::endl; + /*MSVC trouble: std::*/abort(); + } +} + +template< typename T > void assertEqual(T const & value, T const & argument) { + if (argument != value) { + std::cerr + << "assertEqual(" << value << ", " << argument << ") failed" + << std::endl; + /*MSVC trouble: std::*/abort(); + } +} + +} + +sal_Int32 Service::run(css::uno::Sequence< OUString > const &) + throw (css::uno::RuntimeException) +{ + css::uno::Reference< css::lang::XMultiComponentFactory > factory( + m_context->getServiceManager()); + assertTrue(factory.is()); + css::uno::Sequence< css::uno::Any > args(1); + args[0] = css::uno::Reference< css::beans::XPropertySet >( + factory, css::uno::UNO_QUERY_THROW)->getPropertyValue( + OUString("Registry")); + css::uno::Reference< css::container::XHierarchicalNameAccess > provider( + factory->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.stoc.RegistryTypeDescriptionProvider", + args, m_context), + css::uno::UNO_QUERY_THROW); + + // The following assumes that interface members are sorted by increasing + // values of XInterfaceMemberTypeDescription.getPosition, the exceptions + // of interface attributes and interface methods, the constructors of + // services, and the exceptions of service constructors are sorted as given + // in the UNOIDL source code: + + assertEqual< bool >( + false, + provider->hasByHierarchicalName( + OUString( "[][]boolean"))); + assertEqual< bool >( + false, + provider->hasByHierarchicalName( + OUString( "test.registrytdprovider.Struct3<boolean,boolean>"))); + assertEqual< bool >( + false, + provider->hasByHierarchicalName( + OUString( "com.sun.star.uno.XComponentContext::getValueByName"))); + + css::uno::Reference< css::reflection::XCompoundTypeDescription > exception; + exception.set( + provider->getByHierarchicalName( + OUString( "com.sun.star.uno.Exception")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_EXCEPTION, exception->getTypeClass()); + assertEqual( OUString( "com.sun.star.uno.Exception"), + exception->getName()); + assertFalse(exception->getBaseType().is()); + exception.set( + provider->getByHierarchicalName( + OUString( "com.sun.star.uno.RuntimeException")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_EXCEPTION, exception->getTypeClass()); + assertEqual( OUString( "com.sun.star.uno.RuntimeException"), + exception->getName()); + assertEqual( OUString( "com.sun.star.uno.Exception"), + exception->getBaseType()->getName()); + + css::uno::Reference< css::reflection::XStructTypeDescription > structure; + + structure.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct2")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_STRUCT, structure->getTypeClass()); + assertEqual( OUString( "test.registrytdprovider.Struct2"), + structure->getName()); + assertFalse(structure->getBaseType().is()); + assertEqual< sal_Int32 >(1, structure->getMemberTypes().getLength()); + assertEqual( + OUString( "test.registrytdprovider.Struct1"), + structure->getMemberTypes()[0]->getName()); + assertEqual< sal_Int32 >(1, structure->getMemberNames().getLength()); + assertEqual( + OUString("s1"), + structure->getMemberNames()[0]); + assertEqual< sal_Int32 >(0, structure->getTypeParameters().getLength()); + assertEqual< sal_Int32 >(0, structure->getTypeArguments().getLength()); + + structure.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct3")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_STRUCT, structure->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.Struct3"), + structure->getName()); + assertFalse(structure->getBaseType().is()); + assertEqual< sal_Int32 >(1, structure->getMemberTypes().getLength()); + assertEqual( + css::uno::TypeClass_UNKNOWN, + structure->getMemberTypes()[0]->getTypeClass()); + assertEqual( + OUString("U"), + structure->getMemberTypes()[0]->getName()); + assertEqual< sal_Int32 >(1, structure->getMemberNames().getLength()); + assertEqual( + OUString("s2"), + structure->getMemberNames()[0]); + assertEqual< sal_Int32 >(2, structure->getTypeParameters().getLength()); + assertEqual( + OUString("T"), + structure->getTypeParameters()[0]); + assertEqual( + OUString("U"), + structure->getTypeParameters()[1]); + assertEqual< sal_Int32 >(0, structure->getTypeArguments().getLength()); + + structure.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct4")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_STRUCT, structure->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.Struct4"), + structure->getName()); + assertEqual( + OUString( "test.registrytdprovider.Struct2"), + structure->getBaseType()->getName()); + assertEqual< sal_Int32 >(1, structure->getMemberTypes().getLength()); + assertEqual( + OUString( "test.registrytdprovider.Struct3<" + "test.registrytdprovider.Struct2," + "test.registrytdprovider.Struct3<boolean,any>>"), + structure->getMemberTypes()[0]->getName()); + assertEqual< sal_Int32 >(1, structure->getMemberNames().getLength()); + assertEqual( + OUString("s2"), + structure->getMemberNames()[0]); + assertEqual< sal_Int32 >(0, structure->getTypeParameters().getLength()); + assertEqual< sal_Int32 >(0, structure->getTypeArguments().getLength()); + + css::uno::Reference< css::reflection::XInterfaceTypeDescription2 > + interface; + + interface.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.XTest1")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_INTERFACE, interface->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.XTest1"), + interface->getName()); + css::uno::Sequence< + css::uno::Reference< css::reflection::XTypeDescription > > bases( + interface->getBaseTypes()); + assertEqual< sal_Int32 >(1, bases.getLength()); + assertEqual( + OUString( "com.sun.star.uno.XInterface"), + bases[0]->getName()); + css::uno::Sequence< + css::uno::Reference< css::reflection::XTypeDescription > > + optionalBases(interface->getOptionalBaseTypes()); + assertEqual< sal_Int32 >(1, optionalBases.getLength()); + assertEqual( + OUString( + "test.registrytdprovider.XBase"), + optionalBases[0]->getName()); + css::uno::Sequence< + css::uno::Reference< + css::reflection::XInterfaceMemberTypeDescription > > members( + interface->getMembers()); + assertEqual< sal_Int32 >(5, members.getLength()); + + css::uno::Reference< css::reflection::XInterfaceAttributeTypeDescription2 > + attribute; + css::uno::Sequence< + css::uno::Reference< css::reflection::XCompoundTypeDescription > > + getExceptions; + css::uno::Sequence< + css::uno::Reference< css::reflection::XCompoundTypeDescription > > + setExceptions; + css::uno::Reference< css::reflection::XInterfaceMethodTypeDescription > + method; + + attribute.set( members[0], css::uno::UNO_QUERY_THROW); + assertEqual( + css::uno::TypeClass_INTERFACE_ATTRIBUTE, attribute->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.XTest1::a1"), + attribute->getName()); + assertEqual( + OUString("a1"), + attribute->getMemberName()); + assertEqual< sal_Int32 >(3, attribute->getPosition()); + assertEqual< bool >(false, attribute->isReadOnly()); + assertEqual( + OUString("short"), + attribute->getType()->getName()); + assertEqual< bool >(true, attribute->isBound()); + getExceptions = attribute->getGetExceptions(); + assertEqual< sal_Int32 >(0, getExceptions.getLength()); + setExceptions = attribute->getSetExceptions(); + assertEqual< sal_Int32 >(0, setExceptions.getLength()); + + attribute.set( members[1], css::uno::UNO_QUERY_THROW); + assertEqual( + css::uno::TypeClass_INTERFACE_ATTRIBUTE, attribute->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.XTest1::a2"), + attribute->getName()); + assertEqual( + OUString("a2"), + attribute->getMemberName()); + assertEqual< sal_Int32 >(4, attribute->getPosition()); + assertEqual< bool >(false, attribute->isReadOnly()); + assertEqual( + OUString("long"), + attribute->getType()->getName()); + assertEqual< bool >(false, attribute->isBound()); + getExceptions = attribute->getGetExceptions(); + assertEqual< sal_Int32 >(2, getExceptions.getLength()); + assertEqual( + OUString( "com.sun.star.lang.WrappedTargetException"), + getExceptions[0]->getName()); + assertEqual( + OUString( "com.sun.star.uno.RuntimeException"), + getExceptions[1]->getName()); + setExceptions = attribute->getSetExceptions(); + assertEqual< sal_Int32 >(2, setExceptions.getLength()); + assertEqual( + OUString( "com.sun.star.uno.Exception"), + setExceptions[0]->getName()); + assertEqual( + OUString( "com.sun.star.lang.WrappedTargetException"), + setExceptions[1]->getName()); + + attribute.set( members[2], css::uno::UNO_QUERY_THROW); + assertEqual( + css::uno::TypeClass_INTERFACE_ATTRIBUTE, attribute->getTypeClass()); + assertEqual( + OUString( + "test.registrytdprovider.XTest1::a3"), + attribute->getName()); + assertEqual( + OUString("a3"), + attribute->getMemberName()); + assertEqual< sal_Int32 >(5, attribute->getPosition()); + assertEqual< bool >(true, attribute->isReadOnly()); + assertEqual( + OUString("hyper"), + attribute->getType()->getName()); + assertEqual< bool >(true, attribute->isBound()); + getExceptions = attribute->getGetExceptions(); + assertEqual< sal_Int32 >(1, getExceptions.getLength()); + assertEqual( + OUString( "com.sun.star.lang.WrappedTargetException"), + getExceptions[0]->getName()); + setExceptions = attribute->getSetExceptions(); + assertEqual< sal_Int32 >(0, setExceptions.getLength()); + + method.set( members[3], css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_INTERFACE_METHOD, method->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.XTest1::f1"), + method->getName()); + assertEqual( + OUString("f1"), + method->getMemberName()); + assertEqual< sal_Int32 >(6, method->getPosition()); + assertEqual< bool >(false, method->isOneway()); + assertEqual< sal_Int32 >(1, method->getParameters().getLength()); + assertEqual( + OUString("p"), + method->getParameters()[0]->getName()); + assertEqual( + OUString("any"), + method->getParameters()[0]->getType()->getName()); + assertEqual< bool >(false, method->getParameters()[0]->isIn()); + assertEqual< bool >(true, method->getParameters()[0]->isOut()); + assertEqual< sal_Int32 >(0, method->getParameters()[0]->getPosition()); + assertEqual< sal_Int32 >(1, method->getExceptions().getLength()); + assertEqual( + OUString( "com.sun.star.uno.RuntimeException"), + method->getExceptions()[0]->getName()); + + method.set( members[4], css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_INTERFACE_METHOD, method->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.XTest1::f2"), + method->getName()); + assertEqual( + OUString("f2"), + method->getMemberName()); + assertEqual< sal_Int32 >(7, method->getPosition()); + assertEqual< bool >(true, method->isOneway()); + assertEqual< sal_Int32 >(0, method->getParameters().getLength()); + assertEqual< sal_Int32 >(0, method->getExceptions().getLength()); + + interface.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.XTest2")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_INTERFACE, interface->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.XTest2"), + interface->getName()); + assertEqual< sal_Int32 >(1, interface->getBaseTypes().getLength()); + assertEqual( + OUString( "test.registrytdprovider.Typedef2"), + interface->getBaseTypes()[0]->getName()); + assertEqual< sal_Int32 >(0, interface->getOptionalBaseTypes().getLength()); + assertEqual< sal_Int32 >(0, interface->getMembers().getLength()); + + css::uno::Reference< css::reflection::XServiceTypeDescription2 > service; + + service.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Service1")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SERVICE, service->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.Service1"), + service->getName()); + assertEqual< sal_Int32 >(0, service->getMandatoryServices().getLength()); + assertEqual< sal_Int32 >(0, service->getOptionalServices().getLength()); + assertEqual< sal_Int32 >(0, service->getMandatoryInterfaces().getLength()); + assertEqual< sal_Int32 >(0, service->getOptionalInterfaces().getLength()); + assertEqual< bool >(true, service->isSingleInterfaceBased()); + assertEqual( + OUString( + "test.registrytdprovider.XTest1"), + service->getInterface()->getName()); + assertEqual< sal_Int32 >(2, service->getConstructors().getLength()); + assertEqual( + OUString("c1"), + service->getConstructors()[0]->getName()); + assertEqual< sal_Int32 >( + 0, service->getConstructors()[0]->getParameters().getLength()); + assertEqual< sal_Int32 >( + 0, service->getConstructors()[0]->getExceptions().getLength()); + assertEqual( + OUString("c2"), + service->getConstructors()[1]->getName()); + assertEqual< sal_Int32 >( + 1, service->getConstructors()[1]->getParameters().getLength()); + assertEqual( + OUString("p"), + service->getConstructors()[1]->getParameters()[0]->getName()); + assertEqual( + OUString("any"), + (service->getConstructors()[1]->getParameters()[0]->getType()-> + getName())); + assertEqual< bool >( + true, service->getConstructors()[1]->getParameters()[0]->isIn()); + assertEqual< bool >( + false, service->getConstructors()[1]->getParameters()[0]->isOut()); + assertEqual< sal_Int32 >( + 0, service->getConstructors()[1]->getParameters()[0]->getPosition()); + assertEqual< bool >( + true, + service->getConstructors()[1]->getParameters()[0]->isRestParameter()); + assertEqual< sal_Int32 >( + 1, service->getConstructors()[1]->getExceptions().getLength()); + assertEqual( + OUString( "com.sun.star.uno.RuntimeException"), + service->getConstructors()[1]->getExceptions()[0]->getName()); + + service.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Service2")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SERVICE, service->getTypeClass()); + assertEqual( + OUString( "test.registrytdprovider.Service2"), + service->getName()); + assertEqual< sal_Int32 >(0, service->getMandatoryServices().getLength()); + assertEqual< sal_Int32 >(0, service->getOptionalServices().getLength()); + assertEqual< sal_Int32 >(1, service->getMandatoryInterfaces().getLength()); + assertEqual( + OUString( "test.registrytdprovider.XTest1"), + service->getMandatoryInterfaces()[0]->getName()); + assertEqual< sal_Int32 >(1, service->getOptionalInterfaces().getLength()); + assertEqual( + OUString( "test.registrytdprovider.XBase"), + service->getOptionalInterfaces()[0]->getName()); + assertEqual< bool >(false, service->isSingleInterfaceBased()); + assertFalse(service->getInterface().is()); + assertEqual< sal_Int32 >(0, service->getConstructors().getLength()); + + service.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Service3")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SERVICE, service->getTypeClass()); + assertEqual( + OUString( + "test.registrytdprovider.Service3"), + service->getName()); + assertEqual< sal_Int32 >(0, service->getMandatoryServices().getLength()); + assertEqual< sal_Int32 >(0, service->getOptionalServices().getLength()); + assertEqual< sal_Int32 >(0, service->getMandatoryInterfaces().getLength()); + assertEqual< sal_Int32 >(0, service->getOptionalInterfaces().getLength()); + assertEqual< bool >(true, service->isSingleInterfaceBased()); + assertEqual( + OUString( + "test.registrytdprovider.Typedef2"), + service->getInterface()->getName()); + assertEqual< sal_Int32 >(0, service->getConstructors().getLength()); + + css::uno::Reference< css::reflection::XSingletonTypeDescription2 > + singleton; + + singleton.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Singleton1")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SINGLETON, singleton->getTypeClass()); + assertEqual( + OUString( + "test.registrytdprovider.Singleton1"), + singleton->getName()); + assertFalse(singleton->getService().is()); + assertEqual< bool >(true, singleton->isInterfaceBased()); + assertEqual( + OUString( + "test.registrytdprovider.XTest1"), + singleton->getInterface()->getName()); + + singleton.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Singleton2")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SINGLETON, singleton->getTypeClass()); + assertEqual( + OUString( + "test.registrytdprovider.Singleton2"), + singleton->getName()); + assertEqual( + OUString( + "test.registrytdprovider.Service1"), + singleton->getService()->getName()); + assertEqual< bool >(false, singleton->isInterfaceBased()); + assertFalse(singleton->getInterface().is()); + + singleton.set( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Singleton3")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SINGLETON, singleton->getTypeClass()); + assertEqual( + OUString( + "test.registrytdprovider.Singleton3"), + singleton->getName()); + assertFalse(singleton->getService().is()); + assertEqual< bool >(true, singleton->isInterfaceBased()); + assertEqual( + OUString( + "test.registrytdprovider.Typedef2"), + singleton->getInterface()->getName()); + + css::uno::Reference< css::reflection::XPublished > published; + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Enum1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Enum2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct3")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XStructTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct3")), + css::uno::UNO_QUERY_THROW)->getMemberTypes()[0], + css::uno::UNO_QUERY); + assertFalse(published.is()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Struct3a")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Exception1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Exception2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.XTest1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.XTest2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Typedef1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Typedef2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + //TODO: check constants test.registrytdprovider.Const1 (published), + // test.registrytdprovider.Const2 (unpublished), and + // test.registrytdprovider.Consts1.C (no XPublished), which are not + // accessible via provider->getByHierarchicalName (see #i31428) + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Consts1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Consts2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertFalse(published.is()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Service1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Service2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Singleton2")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertTrue(published->isPublished()); + published.set( + css::uno::Reference< css::reflection::XTypeDescription >( + provider->getByHierarchicalName( + OUString( "test.registrytdprovider.Singleton1")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY); + assertTrue(published.is()); + assertFalse(published->isPublished()); + + return 0; +} + +OUString Service::getImplementationName() { + return OUString("test.registrytdprovider.impl"); +} + +css::uno::Sequence< OUString > Service::getSupportedServiceNames() { + return css::uno::Sequence< OUString >(); +} + +css::uno::Reference< css::uno::XInterface > Service::createInstance( + css::uno::Reference< css::uno::XComponentContext > const & context) + throw (css::uno::Exception) +{ + return cppu::getXWeak(new Service(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory(char const * implName, + void * serviceManager, void *) { + void * p = 0; + if (serviceManager != 0) { + css::uno::Reference< css::lang::XSingleComponentFactory > f; + if (Service::getImplementationName().equalsAscii(implName)) { + f = cppu::createSingleComponentFactory( + &Service::createInstance, Service::getImplementationName(), + Service::getSupportedServiceNames()); + } + if (f.is()) { + f->acquire(); + p = f.get(); + } + } + return p; +} + +namespace { + +bool writeInfo(void * registryKey, OUString const & implementationName, + css::uno::Sequence< OUString > const & serviceNames) { + OUString keyName = "/" + implementationName + "/UNO/SERVICES"; + css::uno::Reference< css::registry::XRegistryKey > key; + try { + key = static_cast< css::registry::XRegistryKey * >(registryKey)-> + createKey(keyName); + } catch (css::registry::InvalidRegistryException &) {} + if (!key.is()) { + return false; + } + bool success = true; + for (auto const& rServiceName : serviceNames) { + try { + key->createKey(rServiceName); + } catch (css::registry::InvalidRegistryException &) { + success = false; + break; + } + } + return success; +} + +} + +extern "C" sal_Bool SAL_CALL component_writeInfo(void *, void * registryKey) { + return registryKey + && writeInfo(registryKey, Service::getImplementationName(), + Service::getSupportedServiceNames()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/registry_tdprovider/types.idl b/stoc/test/registry_tdprovider/types.idl new file mode 100644 index 0000000000..7d37e23221 --- /dev/null +++ b/stoc/test/registry_tdprovider/types.idl @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module registrytdprovider { + +published enum Enum1 { E1 }; + +enum Enum2 { E1 }; + +published struct Struct1 { long s1; }; + +struct Struct2 { Struct1 s1; }; + +published struct Struct3<T, U> { U s2; }; + +struct Struct3a<T, U> { U s2; }; + +struct Struct4: Struct2 { Struct3< Struct2, Struct3< boolean, any > > s2; }; + +published exception Exception1: com::sun::star::uno::Exception {}; + +exception Exception2: com::sun::star::uno::Exception {}; + +published interface XBase {}; + +published typedef XBase Typedef1; + +typedef Typedef1 Typedef2; + +published interface XTest1 { + [optional] interface XBase; + + void f1([out] any p) raises (com::sun::star::uno::RuntimeException); + + void f2(); + + [attribute, bound] short a1; + + [attribute] long a2 { + get raises + (com::sun::star::lang::WrappedTargetException, + com::sun::star::uno::RuntimeException); + set raises + (com::sun::star::uno::Exception, + com::sun::star::lang::WrappedTargetException); + }; + + [attribute, readonly, bound] hyper a3 { + get raises (com::sun::star::lang::WrappedTargetException); + }; +}; + +interface XTest2: Typedef2 {}; + +published service Service1: XTest1 { + c1(); + + c2([in] any... p) raises (com::sun::star::uno::RuntimeException); +}; + +service Service2 { + [optional] interface XBase; + + interface XTest1; +}; + +service Service3: Typedef2 {}; + +singleton Singleton1: XTest1; + +published singleton Singleton2 { service Service1; }; + +singleton Singleton3: Typedef2; + +published const long Const1 = 0; + +const long Const2 = 0; + +published constants Consts1 { const long C = 0; }; + +constants Consts2 { const long C = 0; }; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/makefile.mk b/stoc/test/tdmanager/makefile.mk new file mode 100644 index 0000000000..47cc135ba7 --- /dev/null +++ b/stoc/test/tdmanager/makefile.mk @@ -0,0 +1,66 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PRJ := ..$/.. +PRJNAME := stoc + +TARGET := test_tdmanager + +ENABLE_EXCEPTIONS := TRUE + +.INCLUDE: settings.mk + +DLLPRE = # no leading "lib" on .so files + +SLOFILES = $(SLO)$/testtdmanager.obj + +SHL1TARGET = $(ENFORCEDSHLPREFIX)testtdmanager.uno +SHL1OBJS = $(SLOFILES) +SHL1VERSIONMAP = $(SOLARENV)/src/component.map +SHL1STDLIBS = $(CPPULIB) $(CPPUHELPERLIB) $(SALLIB) +SHL1IMPLIB = itesttdmanager +DEF1NAME = $(SHL1TARGET) + +.INCLUDE: target.mk + +ALLTAR: test + +$(MISC)$/$(TARGET)$/%.rdb : %.idl + - rm $@ + - $(MKDIR) $(MISC)$/$(TARGET) + idlc -O$(MISC)$/$(TARGET) -I$(SOLARIDLDIR) -C -cid -we $< + regmerge $@ /UCR $(subst,.rdb,.urd $@) + +IDL_FILES = \ + types.idl \ + types2_incomp.idl \ + types3_incomp.idl \ + types4_incomp.idl \ + types5_incomp.idl \ + types5.idl \ + types6_incomp.idl + +RDB_FILES = $(foreach,i,$(subst,.idl,.rdb $(IDL_FILES)) $(MISC)$/$(TARGET)$/$i) + +$(SLOFILES): $(RDB_FILES) + +test .PHONY: $(SHL1TARGETN) $(RDB_FILES) + uno -c test.tdmanager.impl -l $(subst,$/,/ $(SHL1TARGETN)) \ + -ro $(subst,$/,/ $(SOLARBINDIR)$/udkapi_doc.rdb) \ + -- $(subst,$/,/ $(SOLARBINDIR)$/types_doc.rdb) \ + $(subst,$/,/ $(RDB_FILES)) diff --git a/stoc/test/tdmanager/readme.txt b/stoc/test/tdmanager/readme.txt new file mode 100644 index 0000000000..f497720496 --- /dev/null +++ b/stoc/test/tdmanager/readme.txt @@ -0,0 +1,4 @@ +This test uses the delivered typemgr.uno dynamic library, not the local one. +(It might work to fix this, changing the test from a UNO component started from +the uno executable to a stand-alone application that bootstraps UNO in a special +way.) diff --git a/stoc/test/tdmanager/testtdmanager.cxx b/stoc/test/tdmanager/testtdmanager.cxx new file mode 100644 index 0000000000..7c9971d6aa --- /dev/null +++ b/stoc/test/tdmanager/testtdmanager.cxx @@ -0,0 +1,332 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XSet.hpp> +#include <com/sun/star/lang/XMain.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/reflection/XIndirectTypeDescription.hpp> +#include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp> +#include <com/sun/star/reflection/XPublished.hpp> +#include <com/sun/star/reflection/XStructTypeDescription.hpp> +#include <com/sun/star/reflection/XTypeDescription.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/TypeClass.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/file.h> +#include <osl/thread.h> +#include <rtl/textenc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <uno/environment.h> + +#include /*MSVC trouble: <cstdlib>*/ <stdlib.h> +#include <iostream> +#include <ostream> + +namespace { + +class Service: public cppu::WeakImplHelper< css::lang::XMain > { +public: + virtual sal_Int32 SAL_CALL + run(css::uno::Sequence< OUString > const & arguments) + throw (css::uno::RuntimeException); + + static OUString getImplementationName(); + + static css::uno::Sequence< OUString > getSupportedServiceNames(); + + static css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( + css::uno::Reference< css::uno::XComponentContext > const & context) + throw (css::uno::Exception); + +private: + explicit Service( + css::uno::Reference< css::uno::XComponentContext > const & context): + m_context(context) + {} + + css::uno::Reference< css::uno::XComponentContext > m_context; +}; + +} + +namespace { + +std::ostream & operator <<(std::ostream & out, OUString const & value) { + return out << OUStringToOString(value, RTL_TEXTENCODING_UTF8).getStr(); +} + +void assertTrue(bool argument) { + if (!argument) { + std::cerr + << "assertTrue(" << argument << ") failed" << std::endl; + /*MSVC trouble: std::*/abort(); + } +} + +void assertFalse(bool argument) { + if (argument) { + std::cerr + << "assertFalse(" << argument << ") failed" << std::endl; + /*MSVC trouble: std::*/abort(); + } +} + +template< typename T > void assertEqual(T const & value, T const & argument) { + if (argument != value) { + std::cerr + << "assertEqual(" << value << ", " << argument << ") failed" + << std::endl; + /*MSVC trouble: std::*/abort(); + } +} + +} + +sal_Int32 Service::run(css::uno::Sequence< OUString > const & arguments) + throw (css::uno::RuntimeException) +{ + css::uno::Reference< css::lang::XMultiComponentFactory > factory( + m_context->getServiceManager()); + assertTrue(factory.is()); + css::uno::Reference< css::container::XHierarchicalNameAccess > manager( + m_context->getValueByName( + OUString( + "/singletons/" + "com.sun.star.reflection.theTypeDescriptionManager")), + css::uno::UNO_QUERY_THROW); + + + // test: add cmd line rdbs to manager + + + OSL_ASSERT( arguments.getLength() > 0 ); + css::uno::Reference<css::container::XSet> xSet( + manager, css::uno::UNO_QUERY_THROW ); + for ( sal_Int32 argPos = 0; argPos < arguments.getLength(); ++argPos ) { + OUString url; + OSL_VERIFY( osl_File_E_None == osl_getFileURLFromSystemPath( + arguments[argPos].pData, &url.pData ) ); + bool supposedToBeCompatible = ! url.endsWithIgnoreAsciiCase("_incomp.rdb"); + + css::uno::Reference<css::registry::XSimpleRegistry> xReg( + m_context->getServiceManager()->createInstanceWithContext( + "com.sun.star.registry.SimpleRegistry", + m_context ), css::uno::UNO_QUERY_THROW ); + xReg->open( url, true /* read-only */, false /* ! create */ ); + css::uno::Any arg( css::uno::makeAny(xReg) ); + css::uno::Reference<css::container::XHierarchicalNameAccess> xTDprov( + m_context->getServiceManager()-> + createInstanceWithArgumentsAndContext( + "com.sun.star.comp.stoc.RegistryTypeDescriptionProvider", + css::uno::Sequence<css::uno::Any>( &arg, 1 ), m_context ), + css::uno::UNO_QUERY_THROW ); + try { + xSet->insert( css::uno::makeAny(xTDprov) ); + if (! supposedToBeCompatible) + std::cerr << "current rdb file: " << + OUStringToOString( + url, osl_getThreadTextEncoding()).getStr() << std::endl; + assertTrue(supposedToBeCompatible); + } catch (css::lang::IllegalArgumentException &) { + if (supposedToBeCompatible) + throw; + assertFalse(supposedToBeCompatible); + } + } + + / + + css::uno::Reference< css::reflection::XIndirectTypeDescription > sequence( + manager->getByHierarchicalName( + OUString("[][]boolean")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_SEQUENCE, sequence->getTypeClass()); + assertEqual( + OUString("[][]boolean"), + sequence->getName()); + assertEqual( + OUString("[]boolean"), + sequence->getReferencedType()->getName()); + + css::uno::Reference< css::reflection::XStructTypeDescription > structure( + manager->getByHierarchicalName( + OUString( "test.tdmanager.Struct<boolean,test.tdmanager.Struct<" + "any,com.sun.star.uno.XInterface>>")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_STRUCT, structure->getTypeClass()); + assertEqual( + OUString( "test.tdmanager.Struct<boolean,test.tdmanager.Struct<" + "any,com.sun.star.uno.XInterface>>"), + structure->getName()); + assertEqual< bool >(false, structure->getBaseType().is()); + assertEqual< sal_Int32 >(1, structure->getMemberTypes().getLength()); + assertEqual( + OUString( "test.tdmanager.Struct<any,com.sun.star.uno.XInterface>"), + structure->getMemberTypes()[0]->getName()); + assertEqual< sal_Int32 >(1, structure->getMemberNames().getLength()); + assertEqual( + OUString("s"), + structure->getMemberNames()[0]); + assertEqual< sal_Int32 >(0, structure->getTypeParameters().getLength()); + assertEqual< sal_Int32 >(2, structure->getTypeArguments().getLength()); + assertEqual( + OUString("boolean"), + structure->getTypeArguments()[0]->getName()); + assertEqual( + OUString( "test.tdmanager.Struct<any,com.sun.star.uno.XInterface>"), + structure->getTypeArguments()[1]->getName()); + + css::uno::Reference< css::reflection::XInterfaceMethodTypeDescription > + method( + manager->getByHierarchicalName( + OUString( "com.sun.star.uno.XComponentContext::getValueByName")), + css::uno::UNO_QUERY_THROW); + assertEqual(css::uno::TypeClass_INTERFACE_METHOD, method->getTypeClass()); + assertEqual( + OUString( "com.sun.star.uno.XComponentContext::getValueByName"), + method->getName()); + assertEqual( + OUString("getValueByName"), + method->getMemberName()); + assertEqual< sal_Int32 >(3, method->getPosition()); + assertEqual( + OUString("any"), + method->getReturnType()->getName()); + assertEqual< bool >(false, method->isOneway()); + assertEqual< sal_Int32 >(1, method->getParameters().getLength()); + assertEqual( + OUString("Name"), + method->getParameters()[0]->getName()); + assertEqual( + OUString("string"), + method->getParameters()[0]->getType()->getName()); + assertEqual< bool >(true, method->getParameters()[0]->isIn()); + assertEqual< bool >(false, method->getParameters()[0]->isOut()); + assertEqual< sal_Int32 >(0, method->getParameters()[0]->getPosition()); + assertEqual< sal_Int32 >(0, method->getExceptions().getLength()); + + assertFalse( + css::uno::Reference< css::reflection::XPublished >( + css::uno::Reference< css::reflection::XTypeDescription >( + manager->getByHierarchicalName( + OUString("[]boolean")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY).is()); + assertFalse( + css::uno::Reference< css::reflection::XPublished >( + css::uno::Reference< css::reflection::XTypeDescription >( + manager->getByHierarchicalName( + OUString( "com.sun.star.beans.XIntroTest::ObjectName")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY).is()); + assertFalse( + css::uno::Reference< css::reflection::XPublished >( + css::uno::Reference< css::reflection::XTypeDescription >( + manager->getByHierarchicalName( + OUString( "com.sun.star.beans.XIntroTest::writeln")), + css::uno::UNO_QUERY_THROW), + css::uno::UNO_QUERY).is()); + //TODO: check that the reflection of a property of an accumulation-based + // service does not support XPublished + + return 0; +} + +OUString Service::getImplementationName() { + return OUString("test.tdmanager.impl"); +} + +css::uno::Sequence< OUString > Service::getSupportedServiceNames() { + return css::uno::Sequence< OUString >(); +} + +css::uno::Reference< css::uno::XInterface > Service::createInstance( + css::uno::Reference< css::uno::XComponentContext > const & context) + throw (css::uno::Exception) +{ + return cppu::getXWeak(new Service(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory(char const * implName, + void * serviceManager, void *) { + void * p = 0; + if (serviceManager != 0) { + css::uno::Reference< css::lang::XSingleComponentFactory > f; + if (Service::getImplementationName().equalsAscii(implName)) { + f = cppu::createSingleComponentFactory( + &Service::createInstance, Service::getImplementationName(), + Service::getSupportedServiceNames()); + } + if (f.is()) { + f->acquire(); + p = f.get(); + } + } + return p; +} + +namespace { + +bool writeInfo(void * registryKey, OUString const & implementationName, + css::uno::Sequence< OUString > const & serviceNames) { + OUString keyName = "/" + implementationName + "/UNO/SERVICES"; + css::uno::Reference< css::registry::XRegistryKey > key; + try { + key = static_cast< css::registry::XRegistryKey * >(registryKey)-> + createKey(keyName); + } catch (css::registry::InvalidRegistryException &) {} + if (!key.is()) { + return false; + } + bool success = true; + for (sal_Int32 i = 0; i < serviceNames.getLength(); ++i) { + try { + key->createKey(serviceNames[i]); + } catch (css::registry::InvalidRegistryException &) { + success = false; + break; + } + } + return success; +} + +} + +extern "C" sal_Bool SAL_CALL component_writeInfo(void *, void * registryKey) { + return registryKey + && writeInfo(registryKey, Service::getImplementationName(), + Service::getSupportedServiceNames()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types.idl b/stoc/test/tdmanager/types.idl new file mode 100644 index 0000000000..612ad1bac4 --- /dev/null +++ b/stoc/test/tdmanager/types.idl @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module tdmanager { + +struct Struct<T, U> { U s; }; + +published interface XAnother { + void f(); +}; + +service MyService { + [optional] interface XAnother; + [property, optional] boolean b; +}; + +const long CCC = 5; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types2_incomp.idl b/stoc/test/tdmanager/types2_incomp.idl new file mode 100644 index 0000000000..cb3b0e02c0 --- /dev/null +++ b/stoc/test/tdmanager/types2_incomp.idl @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module com { module sun { module star { module text { + +typedef long XTextField; + +}; }; }; }; + +module test { module tdmanager { + +const long CCC = 6; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types3_incomp.idl b/stoc/test/tdmanager/types3_incomp.idl new file mode 100644 index 0000000000..cb2603f6ff --- /dev/null +++ b/stoc/test/tdmanager/types3_incomp.idl @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module tdmanager { + +// added member: +struct Struct<T, U> { U s; long n; }; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types4_incomp.idl b/stoc/test/tdmanager/types4_incomp.idl new file mode 100644 index 0000000000..6711673ba9 --- /dev/null +++ b/stoc/test/tdmanager/types4_incomp.idl @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module tdmanager { + +published interface XAnother { + // wrong name: + void f2(); +}; + +service MyService { + [optional] interface XAnother; + [property, optional] boolean b; +}; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types5.idl b/stoc/test/tdmanager/types5.idl new file mode 100644 index 0000000000..21eda7d389 --- /dev/null +++ b/stoc/test/tdmanager/types5.idl @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module tdmanager { + +published interface XAnother { + void f(); +}; + +service MyService { + [optional] interface XAnother; + // correct order: + [property, optional] boolean b; + [property, optional] boolean b2; +}; + +const long CCC = 5; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types5_incomp.idl b/stoc/test/tdmanager/types5_incomp.idl new file mode 100644 index 0000000000..8f3b5f83c1 --- /dev/null +++ b/stoc/test/tdmanager/types5_incomp.idl @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module tdmanager { + +published interface XAnother { + void f(); +}; + +service MyService { + [optional] interface XAnother; + // wrong order: + [property, optional] boolean b2; + [property, optional] boolean b; +}; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/tdmanager/types6_incomp.idl b/stoc/test/tdmanager/types6_incomp.idl new file mode 100644 index 0000000000..898c9b3d2b --- /dev/null +++ b/stoc/test/tdmanager/types6_incomp.idl @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module test { module tdmanager { + +published interface XAnother { + void f(); +}; + +service MyService { + // made non-optional: + interface XAnother; + [property, optional] boolean b; + [property, optional] boolean b2; +}; + +}; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testconv.cxx b/stoc/test/testconv.cxx new file mode 100644 index 0000000000..5549779db1 --- /dev/null +++ b/stoc/test/testconv.cxx @@ -0,0 +1,682 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <sal/main.h> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <cppuhelper/servicefactory.hxx> + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/reflection/FieldAccessMode.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> + +#include <cmath> +#include <float.h> +#include <stdio.h> + + +using namespace cppu; +using namespace osl; +using namespace css::uno; +using namespace css::lang; +using namespace css::script; +using namespace css::reflection; +using namespace css::registry; + + +const double MIN_DOUBLE = -DBL_MAX; +const double MAX_DOUBLE = DBL_MAX; +const double MIN_FLOAT = -FLT_MAX; +const double MAX_FLOAT = FLT_MAX; + + +static void printValue( const Any & rVal ) +{ + // print value + OString aStr( OUStringToOString( rVal.getValueType().getTypeName(), RTL_TEXTENCODING_ISO_8859_1 ) ); + printf( "(%s)", aStr.getStr() ); + + switch (rVal.getValueTypeClass()) + { + case TypeClass_VOID: + printf( "void" ); + break; + case TypeClass_ANY: + if (rVal.hasValue()) + printValue( *(Any *)rVal.getValue() ); + break; + case TypeClass_BOOLEAN: + printf( "%s", (*(sal_Bool *)rVal.getValue() ? "true" : "false") ); + break; + case TypeClass_CHAR: + { + char ar[2]; + ar[0] = (char)*(sal_Unicode *)rVal.getValue(); + ar[1] = 0; + printf( "%s", ar ); + break; + } + case TypeClass_BYTE: + printf( "%x", (int)*(sal_Int8 *)rVal.getValue() ); + break; + case TypeClass_SHORT: + printf( "%x", *(sal_Int16 *)rVal.getValue() ); + break; + case TypeClass_UNSIGNED_SHORT: + printf( "%x", *(sal_uInt16 *)rVal.getValue() ); + break; + case TypeClass_LONG: + printf( "%lx", static_cast<long>(*(sal_Int32 *)rVal.getValue()) ); + break; + case TypeClass_UNSIGNED_LONG: + printf( "%lx", static_cast<unsigned long>(*(sal_uInt32 *)rVal.getValue()) ); + break; + case TypeClass_HYPER: + printf( "%lx", (long)*(sal_Int64 *)rVal.getValue() ); + break; + case TypeClass_UNSIGNED_HYPER: + printf( "%lx", (unsigned long)*(sal_uInt64 *)rVal.getValue() ); + break; + case TypeClass_FLOAT: + printf( "%f", *(float *)rVal.getValue() ); + break; + case TypeClass_DOUBLE: + printf( "%g", *(double *)rVal.getValue() ); + break; + case TypeClass_STRING: + { + OString aStr2( OUStringToOString( *(OUString *)rVal.getValue(), RTL_TEXTENCODING_ISO_8859_1 ) ); + printf( aStr2.getStr() ); + break; + } + case TypeClass_ENUM: + { + typelib_EnumTypeDescription * pEnumTD = 0; + TYPELIB_DANGER_GET( (typelib_TypeDescription **)&pEnumTD, rVal.getValueTypeRef() ); + + for ( sal_Int32 nPos = pEnumTD->nEnumValues; nPos--; ) + { + if (pEnumTD->pEnumValues[nPos] == *(int *)rVal.getValue()) + { + printf( OUStringToOString(pEnumTD->ppEnumNames[nPos]->buffer, RTL_TEXTENCODING_ASCII_US).getStr() ); + TYPELIB_DANGER_RELEASE( (typelib_TypeDescription *)pEnumTD ); + return; + } + } + TYPELIB_DANGER_RELEASE( (typelib_TypeDescription *)pEnumTD ); + printf( ">ENUM not found!<" ); + break; + } + case TypeClass_SEQUENCE: + { + uno_Sequence * pSeq = *(uno_Sequence **)rVal.getValue(); + typelib_TypeDescription * pSeqTD = 0; + TYPELIB_DANGER_GET( &pSeqTD, rVal.getValueTypeRef() ); + typelib_TypeDescription * pElemTD = 0; + TYPELIB_DANGER_GET( &pElemTD, ((typelib_IndirectTypeDescription *)pSeqTD)->pType ); + + sal_Int32 nLen = pSeq->nElements; + if (nLen) + { + printf( "{ " ); + for ( sal_Int32 nPos = 0; nPos < nLen; ++nPos ) + { + printValue( Any( ((char *)pSeq->elements) + (nPos * pElemTD->nSize), pElemTD ) ); + if (nPos < (nLen-1)) + printf( ", " ); + } + printf( " }" ); + } + + TYPELIB_DANGER_RELEASE( pElemTD ); + TYPELIB_DANGER_RELEASE( pSeqTD ); + break; + } + + default: + printf( ">not printable<" ); + break; + } +} + +static Reference< XTypeConverter > s_xConverter; + + +static sal_Bool convertTo( const Type & rDestType, const Any & rVal, sal_Bool bExpectSuccess ) +{ + sal_Bool bCanConvert = sal_False; + Any aRet; + + OString aExcMsg; + + try + { + aRet = s_xConverter->convertTo( rVal, rDestType ); + bCanConvert = sal_True; + } + catch (const Exception & rExc) + { + aExcMsg = OUStringToOString( rExc.Message, RTL_TEXTENCODING_ASCII_US ); + } + + if (bExpectSuccess && !bCanConvert) + { + printf( "# conversion of " ); + printValue( rVal ); + printf( " to " ); + printf( OUStringToOString(rDestType.getTypeName(), RTL_TEXTENCODING_ASCII_US).getStr() ); + printf( " failed, but success was expected! [" ); + printf( aExcMsg.getStr() ); + printf( "]\n" ); + aRet = s_xConverter->convertTo( rVal, rDestType ); +#if OSL_DEBUG_LEVEL > 0 + // for debugging, to trace again + try + { + aRet = s_xConverter->convertTo( rVal, rDestType ); + } + catch (Exception &) + { + } +#endif + return sal_False; + } + if (!bExpectSuccess && bCanConvert) + { + printf( "# conversion of " ); + printValue( rVal ); + printf( " to " ); + printValue( aRet ); + printf( " was successful, but was not expected to be!\n" ); +#if OSL_DEBUG_LEVEL > 0 + // for debugging, to trace again + aRet = s_xConverter->convertTo( rVal, rDestType ); +#endif + return sal_False; + } + +#ifdef __RECONVERSION_OUTPUT__ +//= re-conversion output = + if (bCanConvert) + { + // re convert to original type + sal_Bool bReConvert = sal_False; + Any aRet2; + + try + { + aRet2 = s_xConverter->convertTo( aRet, rVal.getValueType() ); + bReConvert = sal_True; + } + catch (const Exception & rExc) + { + aExcMsg = OUStringToOString( rExc.Message, RTL_TEXTENCODING_ISO_8859_1 ); + } + + if (bReConvert) + { + if (rVal != aRet2) + { + printf( "# re-conversion of " ); + printValue( rVal ); + printf( " to " ); + printValue( aRet ); + printf( " to " ); + printValue( aRet2 ); + printf( ": first and last do not match!\n" ); + } + } + else + { + printf( "# re-conversion of " ); + printValue( aRet ); + printf( " to " ); + printf( rVal.getValueType().getTypeName().getStr() ); + printf( " failed! [" ); + printf( aExcMsg.getStr() ); + printf( "]\n" ); + } + } +#endif + + return sal_True; +} + + +typedef struct _ConvBlock +{ + Any _value; + sal_Bool _toString, _toDouble, _toFloat; + sal_Bool _toUINT32, _toINT32, _toUINT16, _toINT16, _toBYTE, _toBOOL, _toChar; + sal_Bool _toTypeClass, _toSeqINT16, _toSeqAny; + + _ConvBlock() + { + } + _ConvBlock( const Any & rValue_, + sal_Bool toString_, sal_Bool toDouble_, sal_Bool toFloat_, + sal_Bool toUINT32_, sal_Bool toINT32_, sal_Bool toUINT16_, sal_Bool toINT16_, + sal_Bool toBYTE_, sal_Bool toBOOL_, sal_Bool toChar_, + sal_Bool toTypeClass_, sal_Bool toSeqINT16_, sal_Bool toSeqAny_ ) + : _value( rValue_ ) + , _toString( toString_ ), _toDouble( toDouble_ ), _toFloat( toFloat_ ) + , _toUINT32( toUINT32_ ), _toINT32( toINT32_ ), _toUINT16( toUINT16_ ), _toINT16( toINT16_ ) + , _toBYTE( toBYTE_ ), _toBOOL( toBOOL_ ), _toChar( toChar_ ) + , _toTypeClass( toTypeClass_ ), _toSeqINT16( toSeqINT16_ ), _toSeqAny( toSeqAny_ ) + { + } +} ConvBlock; + + +static sal_Int32 initBlocks( ConvBlock * pTestBlocks ) +{ + Any aVal; + + sal_uInt32 nElems = 0; + + // ==BYTE== + aVal <<= OUString("0xff"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("255"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_Int8)0xffu; + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("0x80"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("128"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_Int8)( 0x80u ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("0x7f"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + aVal <<= OUString("127"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + aVal <<= (sal_Int8)( 0x7f ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("5"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 ); + aVal <<= OUString("+5"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + aVal <<= (sal_Int8)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("-5"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int8)( -5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("256"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==UINT16== + aVal <<= OUString("65535"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("0xffff"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt16)( 0xffff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("32768"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt16)( 0x8000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("32767"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("0x7fff"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt16)( 0x7fff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("256"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("0x100"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt16)( 0x100 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_uInt16)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_uInt16)( -5 ); // is 0xfffb + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==INT16== + aVal <<= (sal_Int16)( -1 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int16)( -0x8000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int16)( 0x7fff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int16)( 0x100 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int16)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int16)( -5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==UINT32== + aVal <<= OUString("+4294967295"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("4294967295"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("0xffffffff"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt32)( 0xffffffff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("-2147483648"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("-0x80000000"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_uInt32)( 0x80000000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("2147483647"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("0x7fffffff"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt32)( 0x7fffffff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("65536"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= OUString("0x10000"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt32)( 0x10000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_uInt32)( 0x8000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_uInt32)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("0xfffffffb"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (sal_uInt32)( -5 ); // is 0xfffffffb + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==INT32== + aVal <<= (sal_Int32)( 0xffffffff ); // is -1 + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int32)( 0x80000000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int32)( 0x7fffffff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int32)( 0x10000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int32)( -0x8001 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int32)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (sal_Int32)( -5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==FLOAT== + aVal <<= OUString("-3.4e+38"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (float)( MIN_FLOAT ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("+3.4e+38"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (float)( MAX_FLOAT ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("9e-20"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + aVal <<= (float)( 9e-20 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("+.7071067811865"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + aVal <<= (float)( M_SQRT1_2 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("3.14159265359"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + aVal <<= (float)( M_PI ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (float)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==DOUBLE== + aVal <<= OUString("-1.7976931348623155e+308"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (double)( MIN_DOUBLE ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("1.7976931348623155e+308"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (double)( MAX_DOUBLE ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( MIN_FLOAT ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( MAX_FLOAT ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( -((double)0x80000000) ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( -((double)0x80000001) ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( 0x7fffffff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( 0x80000000 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( 0xffffffff ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("0x100000000"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + aVal <<= (double)( SAL_CONST_INT64(0x100000000) ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= (double)( 5 ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==CHAR== + sal_Unicode c = 'A'; + aVal.setValue( &c, cppu::UnoType<cppu::UnoCharType>::get() ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("A"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==BOOL== + aVal <<= OUString("0"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("1"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("False"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("true"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + + sal_Bool bTmp = sal_True; + aVal.setValue( &bTmp, cppu::UnoType<bool>::get() ); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==ZERO STRINGS== + aVal <<= OUString(); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("-"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("-0"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==TYPECLASS ENUM== + aVal <<= OUString("eNuM"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal <<= OUString("DOUBLE"); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + int e = 1; + aVal.setValue( &e, cppu::UnoType<TypeClass>::get()); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + aVal.setValue( &e, cppu::UnoType<FieldAccessMode>::get()); + pTestBlocks[nElems++] = ConvBlock( aVal, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==SEQ of INT== + Sequence< sal_Int32 > aINT32Seq( 3 ), aINT32Seq2( 3 ); + sal_Int32 * pINT32Seq = aINT32Seq.getArray(); + pINT32Seq[0] = -32768; + pINT32Seq[1] = 0; + pINT32Seq[2] = 32767; + aVal <<= aINT32Seq; + pTestBlocks[nElems++] = ConvBlock( aVal, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + pINT32Seq = aINT32Seq2.getArray(); + pINT32Seq[0] = -32768; + pINT32Seq[1] = -32769; + pINT32Seq[2] = 32767; + aVal <<= aINT32Seq2; + pTestBlocks[nElems++] = ConvBlock( aVal, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + // ==SEQ of ANY== + Sequence< Any > aAnySeq( 2 ), aAnySeq2( 2 ); + Any * pAnySeq = aAnySeq.getArray(); + pAnySeq[0] = makeAny( aINT32Seq ); + pAnySeq[1] = makeAny( OUString("lala") ); + aVal <<= aAnySeq; + pTestBlocks[nElems++] = ConvBlock( aVal, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + pAnySeq = aAnySeq2.getArray(); + pAnySeq[0] <<= (sal_Int32)4711; + pAnySeq[1] <<= OUString("0815"); + aVal <<= aAnySeq2; + pTestBlocks[nElems++] = ConvBlock( aVal, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ); + // st,do,fl,u3,i3,u1,i1,by,bo,ch,tc,si,sa + return nElems; +} + + +static void test_Conversion( const Reference< XMultiServiceFactory > & xMgr ) +{ + printf( "test_Conversion(): start...\n" ); + + Reference< XTypeConverter > xConverter( xMgr->createInstance( "com.sun.star.script.Converter" ), UNO_QUERY ); + + ConvBlock * pTestBlocks = new ConvBlock[256]; + sal_Int32 nPos = initBlocks( pTestBlocks ); + + s_xConverter = xConverter; + while (nPos--) + { + const ConvBlock& rBlock = pTestBlocks[nPos]; + const Any & rVal = rBlock._value; + + convertTo( cppu::UnoType<OUString>::get(), rVal, rBlock._toString ); + convertTo( cppu::UnoType<float>::get(), rVal, rBlock._toFloat ); + convertTo( cppu::UnoType<double>::get(), rVal, rBlock._toDouble ); + convertTo( cppu::UnoType<sal_uInt32>::get(), rVal, rBlock._toUINT32 ); + convertTo( cppu::UnoType<sal_Int32>::get(), rVal, rBlock._toINT32 ); + convertTo( cppu::UnoType<cppu::UnoUnsignedShortType>::get(), rVal, rBlock._toUINT16 ); + convertTo( cppu::UnoType<sal_Int16>::get(), rVal, rBlock._toINT16 ); + convertTo( cppu::UnoType<sal_Int8>::get(), rVal, rBlock._toBYTE ); + convertTo( cppu::UnoType<bool>::get(), rVal, rBlock._toBOOL ); + convertTo( cppu::UnoType<cppu::UnoCharType>::get(), rVal, rBlock._toChar ); + convertTo( cppu::UnoType<TypeClass>::get(), rVal, rBlock._toTypeClass ); + convertTo( cppu::UnoType<Sequence< sal_Int16 >>::get(), rVal, rBlock._toSeqINT16 ); + convertTo( cppu::UnoType<Sequence< Any >>::get(), rVal, rBlock._toSeqAny ); + + convertTo( cppu::UnoType<void>::get(), rVal, sal_True ); // anything converts to void + } + s_xConverter.clear(); + + delete [] pTestBlocks; + + Any aRet; + aRet = xConverter->convertTo( Any( &xMgr, cppu::UnoType<XMultiServiceFactory>::get()), + cppu::UnoType<XServiceInfo>::get()); + aRet = xConverter->convertTo( aRet, cppu::UnoType<XMultiServiceFactory>::get()); + aRet = xConverter->convertTo( aRet, cppu::UnoType<XServiceInfo>::get()); + aRet <<= SAL_CONST_INT64(0x7fffffffffffffff); + aRet = xConverter->convertTo( aRet, cppu::UnoType<sal_uInt64>::get()); + OSL_ASSERT( *(const sal_uInt64 *)aRet.getValue() == SAL_CONST_UINT64(0x7fffffffffffffff) ); + aRet <<= SAL_CONST_UINT64(0xffffffffffffffff); + aRet = xConverter->convertTo( aRet, cppu::UnoType<sal_uInt64>::get()); + OSL_ASSERT( *(const sal_uInt64 *)aRet.getValue() == SAL_CONST_UINT64(0xffffffffffffffff) ); + aRet <<= SAL_CONST_INT64(-1); + aRet = xConverter->convertTo( aRet, cppu::UnoType<sal_Int8>::get()); + OSL_ASSERT( *(const sal_Int8 *)aRet.getValue() == (-1) ); + printf( "test_Conversion(): end.\n" ); +} + +SAL_IMPLEMENT_MAIN() +{ + Reference< XMultiServiceFactory > xMgr( createRegistryServiceFactory( OUString("stoctest.rdb") ) ); + + try + { + Reference< XImplementationRegistration > xImplReg( + xMgr->createInstance("com.sun.star.registry.ImplementationRegistration"), UNO_QUERY ); + OSL_ENSURE( xImplReg.is(), "### no impl reg!" ); + + OUString aLibName("stocservices.uno" SAL_DLLEXTENSION ); + xImplReg->registerImplementation( + OUString("com.sun.star.loader.SharedLibrary"), + aLibName, Reference< XSimpleRegistry >() ); + + test_Conversion( xMgr ); + } + catch (const Exception & rExc) + { + DBG_UNHANDLED_EXCEPTION("stoc", "### exception occurred: " << rExc ); + } + + Reference< XComponent >( xMgr, UNO_QUERY )->dispose(); + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testcorefl.cxx b/stoc/test/testcorefl.cxx new file mode 100644 index 0000000000..cd873670b7 --- /dev/null +++ b/stoc/test/testcorefl.cxx @@ -0,0 +1,377 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <sal/log.hxx> +#include <sal/main.h> +#include <osl/diagnose.h> + +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> + +#include <ModuleA/XInterface1.hpp> +#include <ModuleC/XInterfaceA.hpp> +#include <ModuleC/XInterfaceB.hpp> +#include <ModuleC/ModuleC.hpp> + +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> + +#include <rtl/ustrbuf.hxx> + +#include <stdio.h> + +using namespace cppu; +using namespace osl; +using namespace ModuleA; +using namespace ModuleB; +using namespace ModuleC; +using namespace ModuleA::ModuleB; +using namespace com::sun::star; +using namespace css::uno; +using namespace css::lang; +using namespace css::beans; +using namespace css::registry; +using namespace css::reflection; +using namespace css::container; + + +class OInterfaceA : public WeakImplHelper< XInterfaceA > +{ +public: + + virtual void SAL_CALL methodA() throw (RuntimeException) + {} + + virtual void SAL_CALL methodB(sal_Int16 /*aShort*/) throw (RuntimeException) + {} + virtual Sequence< StructB > SAL_CALL methodC(const StructC& /*aStructC*/, StructA& /*aStructA*/) throw (RuntimeException) + { return Sequence< StructB >(); } +}; + + +static inline bool uik_equals( const Uik & rUik1, const Uik & rUik2 ) +{ + return (rUik1.m_Data1 == rUik2.m_Data1 && + rUik1.m_Data2 == rUik2.m_Data2 && + rUik1.m_Data3 == rUik2.m_Data3 && + rUik1.m_Data4 == rUik2.m_Data4 && + rUik1.m_Data5 == rUik2.m_Data5); +} + +static sal_Bool test_corefl( const Reference< XIdlReflection > & xRefl ) +{ + Reference< XIdlClass > xClass; + Reference< XHierarchicalNameAccess > xHNameAccess( xRefl, UNO_QUERY ); + OSL_ENSURE(xHNameAccess.is(), "### cannot get XHierarchicalNameAccess!" ); + + OSL_ENSURE(xRefl->forName("ModuleA.StructA")->getName() == "ModuleA.StructA", "test_RegCoreReflection(): error 2b"); + OSL_ENSURE(xRefl->forName("ModuleA.ExceptionB")->getTypeClass() == TypeClass_EXCEPTION, "test_RegCoreReflection(): error 2c"); + OSL_ENSURE(xRefl->forName("ModuleA.ModuleB.EnumA").is(), "test_RegCoreReflection(): error 2e"); + + + OSL_ENSURE(*(const sal_Bool *)xHNameAccess->getByHierarchicalName(OUString("ModuleC.aConstBoolean")).getValue() == aConstBoolean, "test_RegCoreReflection(): error 4c"); + OSL_ENSURE(*(const sal_Int8 *)xHNameAccess->getByHierarchicalName(OUString("ModuleC.aConstByte")).getValue() == aConstByte, "test_RegCoreReflection(): error 4e"); + OSL_ENSURE(xHNameAccess->getByHierarchicalName(OUString("ModuleC.aConstShort")) == aConstShort, "test_RegCoreReflection(): error 4g"); + OSL_ENSURE(xHNameAccess->getByHierarchicalName(OUString("ModuleC.aConstUShort")) == aConstUShort, "test_RegCoreReflection(): error 4i"); + OSL_ENSURE(xHNameAccess->getByHierarchicalName(OUString("ModuleC.aConstLong")) == aConstLong, "test_RegCoreReflection(): error 4k"); + OSL_ENSURE(xHNameAccess->getByHierarchicalName(OUString("ModuleC.aConstULong")) == aConstULong, "test_RegCoreReflection(): error 4m"); + + // Enums + + xClass = xRefl->forName("ModuleA.ModuleB.EnumA"); + + OSL_ENSURE(xClass.is(), "test_RegCoreReflection(): error 5"); + + Sequence<Reference< XIdlField > > fields = xClass->getFields(); + + OSL_ENSURE( + (fields.getLength() == 3) && + (fields.getArray()[0]->getName() == "VAL_1" ) && + (*(EnumA*)fields.getArray()[0]->get(Any()).getValue() == EnumA_VAL_1) && + (fields.getArray()[1]->getName() == "VAL_2" ) && + (*(EnumA*)fields.getArray()[1]->get(Any()).getValue() == EnumA_VAL_2) && + (fields.getArray()[2]->getName() == "VAL_3" ) && + (*(EnumA*)fields.getArray()[2]->get(Any()).getValue() == EnumA_VAL_3), + "test_RegCoreReflection(): error 6"); + + + // Interface + + Reference< XIdlClass > xA = xRefl->forName("ModuleC.XInterfaceB"); + + xClass = xRefl->forName("ModuleC.XInterfaceB"); + + OSL_ENSURE(xClass == xA, "test_RegCoreReflection(): error 7"); + OSL_ENSURE(xClass.is(), "test_RegCoreReflection(): error 7a"); + + typelib_TypeDescription * pTD = 0; + OUString aModuleName( "ModuleC.XInterfaceB" ); + typelib_typedescription_getByName( &pTD, aModuleName.pData ); + OSL_ENSURE( pTD, "### cannot get typedescription for ModuleC.XInterfaceB!" ); + + OSL_ENSURE( uik_equals( *(Uik *)&((typelib_InterfaceTypeDescription *)pTD)->aUik, + xClass->getUik() ), + "test_RegCoreReflection(): error 8" ); + typelib_typedescription_release( pTD ); + + OSL_ENSURE(xClass->getSuperclasses().getLength() == 1, "test_RegCoreReflection(): error 9"); + OSL_ENSURE(xClass->getSuperclasses().getArray()[0]->getName() == "ModuleC.XInterfaceA", "test_RegCoreReflection(): error 10"); + OSL_ENSURE(xClass->getMethods().getLength() == 7, "test_RegCoreReflection(): error 11"); + OSL_ENSURE(xA->getMethods().getLength() == 7, "test_RegCoreReflection(): error 11a"); + OSL_ENSURE(xClass->getMethods().getArray()[3]->getName() == "methodA", "test_RegCoreReflection(): 12"); + OSL_ENSURE(xClass->getMethods().getArray()[3]->getReturnType()->getTypeClass() == TypeClass_VOID, "test_RegCoreReflection(): error 13"); + OSL_ENSURE(xClass->getMethods().getArray()[3]->getParameterTypes().getLength() == 0, "test_RegCoreReflection(): error 14"); + OSL_ENSURE(xClass->getMethods().getArray()[3]->getExceptionTypes().getLength() == 0, "test_RegCoreReflection(): error 15"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getName() == "methodB", "test_RegCoreReflection(): error 16"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getMode() == MethodMode_ONEWAY, "test_RegCoreReflection(): error 16a"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getReturnType()->getTypeClass() == TypeClass_VOID, "test_RegCoreReflection(): error 16"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getParameterTypes().getLength() == 1, "test_RegCoreReflection(): error 17"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getParameterTypes().getArray()[0]->getTypeClass() == TypeClass_SHORT, "test_RegCoreReflection(): error 18"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getParameterInfos().getArray()[0].aName == "aShort", "test_RegCoreReflection(): error 18a"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getParameterInfos().getArray()[0].aType == xRefl->forName("short"), "test_RegCoreReflection(): error 18b"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getParameterInfos().getArray()[0].aMode == ParamMode_IN, "test_RegCoreReflection(): error 18c"); + OSL_ENSURE(xClass->getMethods().getArray()[4]->getExceptionTypes().getLength() == 0, "test_RegCoreReflection(): error 19"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getName() == "methodC", "test_RegCoreReflection(): error 20"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getMode() == MethodMode_TWOWAY, "test_RegCoreReflection(): error 20a"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getReturnType()->getTypeClass() == TypeClass_SEQUENCE, "test_RegCoreReflection(): error 21"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getReturnType()->getComponentType()->getTypeClass() == TypeClass_STRUCT, "test_RegCoreReflection(): error 22"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getReturnType()->getComponentType()->getName() == "ModuleA.StructB", "test_RegCoreReflection(): error 23"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getParameterTypes().getLength() == 2, "test_RegCoreReflection(): error 24"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getParameterTypes().getArray()[0]->getTypeClass() == TypeClass_STRUCT, "test_RegCoreReflection(): error 25"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getParameterTypes().getArray()[0]->getName() == "ModuleA.StructC", "test_RegCoreReflection(): error 26"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getParameterTypes().getArray()[1]->getTypeClass() == TypeClass_STRUCT, "test_RegCoreReflection(): error 27"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getParameterTypes().getArray()[1]->getName() == "ModuleA.StructA", "test_RegCoreReflection(): error 28"); + OSL_ENSURE(xClass->getMethods().getArray()[5]->getExceptionTypes().getLength() == 0, "test_RegCoreReflection(): error 29"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getName() == "methodD", "test_RegCoreReflection(): error 30"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getReturnType()->getTypeClass() == TypeClass_INTERFACE, "test_RegCoreReflection(): error 31"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getReturnType()->getName() == "ModuleC.XInterfaceA", "test_RegCoreReflection(): error 32"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getParameterTypes().getLength() == 1, "test_RegCoreReflection(): error 33"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getParameterTypes().getArray()[0]->getTypeClass() == TypeClass_ENUM, "test_RegCoreReflection(): error 34"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getParameterTypes().getArray()[0]->getName() == "ModuleA.ModuleB.EnumA", "test_RegCoreReflection(): error 35"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getLength() == 3, "test_RegCoreReflection(): error 36"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[0]->getTypeClass() == TypeClass_EXCEPTION, "test_RegCoreReflection(): error 37"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[0]->getName() == "ModuleA.ExceptionA", "test_RegCoreReflection(): error 38"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[1]->getTypeClass() == TypeClass_EXCEPTION, "test_RegCoreReflection(): error 38"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[1]->getName() == "ModuleA.ExceptionB", "test_RegCoreReflection(): error 39"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getTypeClass() == TypeClass_EXCEPTION, "test_RegCoreReflection(): error 40"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getName() == "ModuleA.ExceptionC", "test_RegCoreReflection(): error 41"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getLength() == 3, "test_RegCoreReflection(): error 42"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getArray()[0]->getType()->getTypeClass() == TypeClass_BOOLEAN, "test_RegCoreReflection(): error 43"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getArray()[0]->getType()->getName() == "boolean", "test_RegCoreReflection(): error 43a"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getArray()[1]->getType()->getTypeClass() == TypeClass_STRUCT, "test_RegCoreReflection(): error 44"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getArray()[1]->getType()->getName() == "ModuleA.StructC", "test_RegCoreReflection(): error 45"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getArray()[2]->getType()->getTypeClass() == TypeClass_INTERFACE, "test_RegCoreReflection(): error 46"); + OSL_ENSURE(xClass->getMethods().getArray()[6]->getExceptionTypes().getArray()[2]->getFields().getArray()[2]->getType()->getName() == "ModuleA.XInterface1", "test_RegCoreReflection(): error 47"); + + // SequenceReflections + + OSL_ENSURE(xRefl->forName("[]ModuleA.StructA")->getTypeClass() == TypeClass_SEQUENCE, "test_RegCoreReflection(): error 48"); + OSL_ENSURE(xRefl->forName("[]ModuleA.StructA")->getComponentType().is(), "test_RegCoreReflection(): error 49"); + OSL_ENSURE(xRefl->forName("[][]ModuleA.StructA")->getComponentType()->getComponentType()->getName() == "ModuleA.StructA", "test_RegCoreReflection(): error 50"); + OSL_ENSURE(xRefl->forName("[]com.sun.star.uno.XInterface") == xRefl->forName("ModuleA.StructC")->getField(OUString("aInterfaceSeq"))->getType(), "test_RegCoreReflection(): error 51"); + + StructC aStructC; + aStructC.aLong = aConstLong; + aStructC.aShort = aConstShort; + aStructC.aFloat = aConstFloat; + aStructC.aDouble = aConstDouble; + aStructC.aInterfaceSeq = Sequence<Reference<XInterface > >(); + + Any aAny; + + xRefl->forName("ModuleA.StructC")->getField(OUString("aInterfaceSeq"))->getType()->createObject(aAny); + + OSL_ENSURE(aAny.getValueType() == cppu::UnoType<Sequence<Reference< XInterface > >>::get(), "test_RegCoreReflection(): error 51a"); + + Any aStructAny(&aStructC, cppu::UnoType<StructC>::get()); + + sal_Int32 nLong = aConstLong * 2; + aAny.setValue( &nLong, cppu::UnoType<sal_Int32>::get()); + + OSL_ENSURE(*(sal_Int32*)xRefl->forName("ModuleA.StructA")->getField(OUString( "aLong" ))->get( + Any(&aStructC, cppu::UnoType<StructC>::get())).getValue() == aConstLong, "test_RegCoreReflection(): error 52"); + OSL_ENSURE(xRefl->forName("ModuleA.StructA")->getField(OUString( "aLong" ))->getAccessMode() == FieldAccessMode_READWRITE, "test_RegCoreReflection(): error 52a"); + Reference< XIdlField2 > rField ( xRefl->forName("ModuleA.StructC")->getField(OUString( "aLong" )) , UNO_QUERY ); + rField->set(aStructAny, aAny); + OSL_ENSURE(*(sal_Int32*)xRefl->forName("ModuleA.StructB")->getField(OUString( "aLong" ))->get(aStructAny).getValue() == *(sal_Int32*)aAny.getValue(), "test_RegCoreReflection(): error 53"); + + xRefl->forName("[]ModuleA.StructA")->createObject(aAny); + + OSL_ENSURE( aAny.getValueTypeName() == "[]ModuleA.StructA", "test_RegCoreReflection(): error 54" ); + xRefl->forName("[][]ModuleA.StructA")->createObject(aAny); + + OSL_ENSURE( aAny.getValueTypeName() == "[][]ModuleA.StructA", "test_RegCoreReflection(): error 56" ); + + OSL_ENSURE(xRefl->forName("[][][]unsigned long")->getComponentType()->getComponentType()->getComponentType()->getTypeClass() == TypeClass_UNSIGNED_LONG, "test_RegCoreReflection(): error 62"); + + try + { + SAL_WARN( "stoc", "case 1" ); + Any bla = xRefl->forName("ModuleA.StructC")->getField(OUString("aString"))->get(Any()); + OSL_ENSURE(sal_False, "test_RegCoreReflection(): error 63"); + return sal_False; + } + catch (IllegalArgumentException &) + { + } + + try + { + SAL_WARN( "stoc", "case 2" ); + Any blup; + blup <<= aStructC; + Any gulp; + rField.set( xRefl->forName("ModuleA.StructC")->getField(OUString("aString")) , UNO_QUERY); + rField->set( blup, gulp); + OSL_ENSURE(sal_False, "test_RegCoreReflection(): error 64"); + return sal_False; + } + catch (IllegalArgumentException &) + { + } + + try + { + SAL_WARN( "stoc", "case 3" ); + Any gulp; + gulp <<= 3.14f; + Any blup; + blup <<= aStructC; + rField.set( xRefl->forName("ModuleA.StructC")->getField(OUString("aString")) , UNO_QUERY); + xRefl->forName("ModuleA.StructC")->getField(OUString("aString"))->set(blup, gulp); + OSL_ENSURE(sal_False, "test_RegCoreReflection(): error 65"); + return sal_False; + } + catch (IllegalArgumentException &) + { + } + + Any gulp; + gulp <<= OUString("Test"); + Any blup; + blup <<= aStructC; + xRefl->forName("ModuleA.StructC")->getField(OUString("aString"))->set(blup, gulp); + + Reference< XInterfaceA > xAI = new OInterfaceA(); + + try + { + Sequence< Any > params; + SAL_WARN( "stoc", "case 4" ); + + Any a; + a <<= xAI; + Any bla = xRefl->forName("ModuleC.XInterfaceA")->getMethod(OUString("methodC"))->invoke(a, params); + OSL_ENSURE(sal_False, "test_RegCoreReflection(): error 66"); + return sal_False; + } + catch (IllegalArgumentException &) + { + } + + StructA aStructA; + + { + Sequence< Any > params(2); + + params.getArray()[0].setValue(&aStructC, cppu::UnoType<StructC>::get()); + params.getArray()[1].setValue(&aStructC, cppu::UnoType<StructC>::get()); + + Any a; + a <<= xAI; + Any bla = xRefl->forName("ModuleC.XInterfaceA")->getMethod(OUString("methodC"))->invoke(a, params); + } + try + { + Sequence< Any > params(2); + + params.getArray()[0].setValue(&aStructA, cppu::UnoType<StructA>::get()); + params.getArray()[1].setValue(&aStructA, cppu::UnoType<StructA>::get()); + + Any a; + a <<= xAI; + Any bla = xRefl->forName("ModuleC.XInterfaceA")->getMethod(OUString("methodC"))->invoke(a, params); + OSL_ENSURE(sal_False, "test_RegCoreReflection(): error 67"); + return sal_False; + } + catch (IllegalArgumentException &) + { + } + + Sequence< Any > params(2); + + params.getArray()[0].setValue(&aStructC, cppu::UnoType<StructC>::get()); + params.getArray()[1].setValue(&aStructA, cppu::UnoType<StructA>::get()); + + Any a; + a <<= xAI; + bool result = (xRefl->forName("ModuleC.XInterfaceA")->getMethod(OUString("methodC"))->invoke(a, params).getValueType() + == cppu::UnoType<Sequence<StructB>>::get()); (void)result; + OSL_ENSURE(result, "test_RegCoreReflection(): error 68"); + + return sal_True; +} + +SAL_IMPLEMENT_MAIN() +{ + sal_Bool bSucc = sal_False; + try + { + OUString aLibName( "reflection.uno" SAL_DLLEXTENSION ); + + Reference< XMultiServiceFactory > xMgr( + createRegistryServiceFactory( + OUString( "stoctest.rdb" ) ) ); + Reference< XComponentContext > xContext; + Reference< beans::XPropertySet > xProps( xMgr, UNO_QUERY ); + OSL_ASSERT( xProps.is() ); + xProps->getPropertyValue( + OUString( "DefaultContext" ) ) >>= + xContext; + OSL_ASSERT( xContext.is() ); + + Reference< XIdlReflection > xRefl; + xContext->getValueByName( + OUString( "/singletons/com.sun.star.reflection.theCoreReflection") ) + >>= xRefl; + OSL_ENSURE( + xRefl.is(), "### CoreReflection singleton not accessible!?" ); + + bSucc = test_corefl( xRefl ); + + Reference< XComponent >( xContext, UNO_QUERY )->dispose(); + } + catch (const Exception & rExc) + { + DBG_UNHANDLED_EXCEPTION("stoc", "### exception occurred: " << rExc ); + } + + printf( "testcorefl %s !\n", (bSucc ? "succeeded" : "failed") ); + return (bSucc ? 0 : -1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testcorefl.idl b/stoc/test/testcorefl.idl new file mode 100644 index 0000000000..c345b725bb --- /dev/null +++ b/stoc/test/testcorefl.idl @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module ModuleA +{ + exception ExceptionA + { + boolean aBoolean; + }; + + struct StructA + { + long aLong; + short aShort; + }; + + struct StructB : StructA + { + double aDouble; + float aFloat; + }; + + struct StructC : StructB + { + string aString; + sequence<com::sun::star::uno::XInterface> aInterfaceSeq; + }; + + exception ExceptionB : ExceptionA + { + StructC aStructC; + }; + + interface XInterface1 : com::sun::star::uno::XInterface + { + }; + + exception ExceptionC : ExceptionB + { + XInterface1 aInterface1; + }; + + module ModuleB + { + enum EnumA { VAL_1, VAL_2, VAL_3 = 17}; + }; + +}; + +module ModuleC +{ + const boolean aConstBoolean = True; + const byte aConstByte = 0; + const short aConstShort = -1; + const unsigned short aConstUShort = 1; + const long aConstLong = -2; + const unsigned long aConstULong = 2; + const float aConstFloat = 3.14; + const double aConstDouble = 3.14e-10; + + + interface XInterfaceA : com::sun::star::uno::XInterface + { + void methodA(); + void methodB([in] short aShort); + sequence<ModuleA::StructB> methodC([in] ModuleA::StructC aStructC, [inout] ModuleA::StructA aStructA); + }; + + interface XInterfaceB : XInterfaceA + { + [attribute] string aString; + + XInterfaceA methodD([in] ModuleA::ModuleB::EnumA aEnumA) raises (ModuleA::ExceptionA, ModuleA::ExceptionB, ModuleA::ExceptionC); + }; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testiadapter.cxx b/stoc/test/testiadapter.cxx new file mode 100644 index 0000000000..ca5afc4029 --- /dev/null +++ b/stoc/test/testiadapter.cxx @@ -0,0 +1,1051 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <stdio.h> + +#include <sal/main.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/weak.hxx> + +#include <test/XLanguageBindingTest.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory2.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +#include <cppuhelper/implbase.hxx> +#include <cmath> + + +using namespace test; +using namespace cppu; +using namespace osl; +using namespace css::uno; +using namespace css::script; +using namespace css::beans; +using namespace css::lang; +using namespace css::reflection; +using namespace css::registry; + + +sal_Bool equals( const test::TestElement & rData1, const test::TestElement & rData2 ) +{ + OSL_ENSURE( rData1.Bool == rData2.Bool, "### bool does not match!" ); + OSL_ENSURE( rData1.Char == rData2.Char, "### char does not match!" ); + OSL_ENSURE( rData1.Byte == rData2.Byte, "### byte does not match!" ); + OSL_ENSURE( rData1.Short == rData2.Short, "### short does not match!" ); + OSL_ENSURE( rData1.UShort == rData2.UShort, "### unsigned short does not match!" ); + OSL_ENSURE( rData1.Long == rData2.Long, "### long does not match!" ); + OSL_ENSURE( rData1.ULong == rData2.ULong, "### unsigned long does not match!" ); + OSL_ENSURE( rData1.Hyper == rData2.Hyper, "### hyper does not match!" ); + OSL_ENSURE( rData1.UHyper == rData2.UHyper, "### unsigned hyper does not match!" ); + OSL_ENSURE( rData1.Float == rData2.Float, "### float does not match!" ); + OSL_ENSURE( rData1.Double == rData2.Double, "### double does not match!" ); + OSL_ENSURE( rData1.Enum == rData2.Enum, "### enum does not match!" ); + OSL_ENSURE( rData1.String == rData2.String, "### string does not match!" ); + OSL_ENSURE( rData1.Interface == rData2.Interface, "### interface does not match!" ); + OSL_ENSURE( rData1.Any == rData2.Any, "### any does not match!" ); + + return (rData1.Bool == rData2.Bool && + rData1.Char == rData2.Char && + rData1.Byte == rData2.Byte && + rData1.Short == rData2.Short && + rData1.UShort == rData2.UShort && + rData1.Long == rData2.Long && + rData1.ULong == rData2.ULong && + rData1.Hyper == rData2.Hyper && + rData1.UHyper == rData2.UHyper && + rData1.Float == rData2.Float && + rData1.Double == rData2.Double && + rData1.Enum == rData2.Enum && + rData1.String == rData2.String && + rData1.Interface == rData2.Interface && + rData1.Any == rData2.Any); +} + +sal_Bool equals( const test::TestData & rData1, const test::TestData & rData2 ) +{ + sal_Int32 nLen; + + if ((rData1.Sequence == rData2.Sequence) && + equals( (const test::TestElement &)rData1, (const test::TestElement &)rData2 ) && + (nLen = rData1.Sequence.getLength()) == rData2.Sequence.getLength()) + { + // once again by hand sequence == + const test::TestElement * pElements1 = rData1.Sequence.getConstArray(); + const test::TestElement * pElements2 = rData2.Sequence.getConstArray(); + for ( ; nLen--; ) + { + if (! equals( pElements1[nLen], pElements2[nLen] )) + { + OSL_FAIL( "### sequence element did not match!" ); + return sal_False; + } + } + return sal_True; + } + return sal_False; +} + +void assign( test::TestElement & rData, + sal_Bool bBool, sal_Unicode cChar, sal_Int8 nByte, + sal_Int16 nShort, sal_uInt16 nUShort, + sal_Int32 nLong, sal_uInt32 nULong, + sal_Int64 nHyper, sal_uInt64 nUHyper, + float fFloat, double fDouble, + test::TestEnum eEnum, const OUString& rStr, + const css::uno::Reference< css::uno::XInterface >& xTest, + const css::uno::Any& rAny ) +{ + rData.Bool = bBool; + rData.Char = cChar; + rData.Byte = nByte; + rData.Short = nShort; + rData.UShort = nUShort; + rData.Long = nLong; + rData.ULong = nULong; + rData.Hyper = nHyper; + rData.UHyper = nUHyper; + rData.Float = fFloat; + rData.Double = fDouble; + rData.Enum = eEnum; + rData.String = rStr; + rData.Interface = xTest; + rData.Any = rAny; +} + +void assign( test::TestData & rData, + sal_Bool bBool, sal_Unicode cChar, sal_Int8 nByte, + sal_Int16 nShort, sal_uInt16 nUShort, + sal_Int32 nLong, sal_uInt32 nULong, + sal_Int64 nHyper, sal_uInt64 nUHyper, + float fFloat, double fDouble, + test::TestEnum eEnum, const OUString& rStr, + const css::uno::Reference< css::uno::XInterface >& xTest, + const css::uno::Any& rAny, + const css::uno::Sequence< test::TestElement >& rSequence ) +{ + assign( (test::TestElement &)rData, + bBool, cChar, nByte, nShort, nUShort, nLong, nULong, nHyper, nUHyper, fFloat, fDouble, + eEnum, rStr, xTest, rAny ); + rData.Sequence = rSequence; +} + + +class Test_Impl : public WeakImplHelper< XLanguageBindingTest > +{ + test::TestData _aData, _aStructData; + +public: + virtual ~Test_Impl() + { SAL_INFO("stoc", "> scalar Test_Impl dtor <" ); } + + // XLBTestBase + virtual void SAL_CALL setValues( sal_Bool bBool, sal_Unicode cChar, sal_Int8 nByte, + sal_Int16 nShort, sal_uInt16 nUShort, + sal_Int32 nLong, sal_uInt32 nULong, + sal_Int64 nHyper, sal_uInt64 nUHyper, + float fFloat, double fDouble, + test::TestEnum eEnum, const OUString& rStr, + const css::uno::Reference< css::uno::XInterface >& xTest, + const css::uno::Any& rAny, + const css::uno::Sequence<test::TestElement >& rSequence, + const test::TestData& rStruct ) + throw(css::uno::RuntimeException); + + virtual test::TestData SAL_CALL setValues2( sal_Bool& bBool, sal_Unicode& cChar, sal_Int8& nByte, + sal_Int16& nShort, sal_uInt16& nUShort, + sal_Int32& nLong, sal_uInt32& nULong, + sal_Int64& nHyper, sal_uInt64& nUHyper, + float& fFloat, double& fDouble, + test::TestEnum& eEnum, OUString& rStr, + css::uno::Reference< css::uno::XInterface >& xTest, + css::uno::Any& rAny, + css::uno::Sequence<test::TestElement >& rSequence, + test::TestData& rStruct ) + throw(css::uno::RuntimeException); + + virtual test::TestData SAL_CALL getValues( sal_Bool& bBool, sal_Unicode& cChar, sal_Int8& nByte, + sal_Int16& nShort, sal_uInt16& nUShort, + sal_Int32& nLong, sal_uInt32& nULong, + sal_Int64& nHyper, sal_uInt64& nUHyper, + float& fFloat, double& fDouble, + test::TestEnum& eEnum, OUString& rStr, + css::uno::Reference< css::uno::XInterface >& xTest, + css::uno::Any& rAny, + css::uno::Sequence< test::TestElement >& rSequence, + test::TestData& rStruct ) + throw(css::uno::RuntimeException); + + virtual sal_Bool SAL_CALL getBool() throw(css::uno::RuntimeException) + { return _aData.Bool; } + virtual sal_Int8 SAL_CALL getByte() throw(css::uno::RuntimeException) + { return _aData.Byte; } + virtual sal_Unicode SAL_CALL getChar() throw(css::uno::RuntimeException) + { return _aData.Char; } + virtual sal_Int16 SAL_CALL getShort() throw(css::uno::RuntimeException) + { return _aData.Short; } + virtual sal_uInt16 SAL_CALL getUShort() throw(css::uno::RuntimeException) + { return _aData.UShort; } + virtual sal_Int32 SAL_CALL getLong() throw(css::uno::RuntimeException) + { return _aData.Long; } + virtual sal_uInt32 SAL_CALL getULong() throw(css::uno::RuntimeException) + { return _aData.ULong; } + virtual sal_Int64 SAL_CALL getHyper() throw(css::uno::RuntimeException) + { return _aData.Hyper; } + virtual sal_uInt64 SAL_CALL getUHyper() throw(css::uno::RuntimeException) + { return _aData.UHyper; } + virtual float SAL_CALL getFloat() throw(css::uno::RuntimeException) + { return _aData.Float; } + virtual double SAL_CALL getDouble() throw(css::uno::RuntimeException) + { return _aData.Double; } + virtual test::TestEnum SAL_CALL getEnum() throw(css::uno::RuntimeException) + { return _aData.Enum; } + virtual OUString SAL_CALL getString() throw(css::uno::RuntimeException) + { return _aData.String; } + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getInterface( ) throw(css::uno::RuntimeException) + { return _aData.Interface; } + virtual css::uno::Any SAL_CALL getAny() throw(css::uno::RuntimeException) + { return _aData.Any; } + virtual css::uno::Sequence< test::TestElement > SAL_CALL getSequence() throw(css::uno::RuntimeException) + { return _aData.Sequence; } + virtual test::TestData SAL_CALL getStruct() throw(css::uno::RuntimeException) + { return _aStructData; } + + virtual void SAL_CALL setBool( sal_Bool _bool ) throw(css::uno::RuntimeException) + { _aData.Bool = _bool; } + virtual void SAL_CALL setByte( sal_Int8 _byte ) throw(css::uno::RuntimeException) + { _aData.Byte = _byte; } + virtual void SAL_CALL setChar( sal_Unicode _char ) throw(css::uno::RuntimeException) + { _aData.Char = _char; } + virtual void SAL_CALL setShort( sal_Int16 _short ) throw(css::uno::RuntimeException) + { _aData.Short = _short; } + virtual void SAL_CALL setUShort( sal_uInt16 _ushort ) throw(css::uno::RuntimeException) + { _aData.UShort = _ushort; } + virtual void SAL_CALL setLong( sal_Int32 _long ) throw(css::uno::RuntimeException) + { _aData.Long = _long; } + virtual void SAL_CALL setULong( sal_uInt32 _ulong ) throw(css::uno::RuntimeException) + { _aData.ULong = _ulong; } + virtual void SAL_CALL setHyper( sal_Int64 _hyper ) throw(css::uno::RuntimeException) + { _aData.Hyper = _hyper; } + virtual void SAL_CALL setUHyper( sal_uInt64 _uhyper ) throw(css::uno::RuntimeException) + { _aData.UHyper = _uhyper; } + virtual void SAL_CALL setFloat( float _float ) throw(css::uno::RuntimeException) + { _aData.Float = _float; } + virtual void SAL_CALL setDouble( double _double ) throw(css::uno::RuntimeException) + { _aData.Double = _double; } + virtual void SAL_CALL setEnum( test::TestEnum _enum ) throw(css::uno::RuntimeException) + { _aData.Enum = _enum; } + virtual void SAL_CALL setString( const OUString& _string ) throw(css::uno::RuntimeException) + { _aData.String = _string; } + virtual void SAL_CALL setInterface( const css::uno::Reference< css::uno::XInterface >& _interface ) throw(css::uno::RuntimeException) + { _aData.Interface = _interface; } + virtual void SAL_CALL setAny( const css::uno::Any& _any ) throw(css::uno::RuntimeException) + { _aData.Any = _any; } + virtual void SAL_CALL setSequence( const css::uno::Sequence<test::TestElement >& _sequence ) throw(css::uno::RuntimeException) + { _aData.Sequence = _sequence; } + virtual void SAL_CALL setStruct( const test::TestData& _struct ) throw(css::uno::RuntimeException) + { _aStructData = _struct; } + + // XLanguageBindingTest + virtual test::TestData SAL_CALL raiseException( sal_Bool& bBool, sal_Unicode& cChar, sal_Int8& nByte, sal_Int16& nShort, sal_uInt16& nUShort, sal_Int32& nLong, sal_uInt32& nULong, sal_Int64& nHyper, sal_uInt64& nUHyper, float& fFloat, double& fDouble, test::TestEnum& eEnum, OUString& aString, css::uno::Reference< css::uno::XInterface >& xInterface, css::uno::Any& aAny, css::uno::Sequence<test::TestElement >& aSequence,test::TestData& aStruct ) + throw(css::lang::IllegalArgumentException, css::uno::RuntimeException); + + virtual sal_Int32 SAL_CALL getRuntimeException() throw(css::uno::RuntimeException); + virtual void SAL_CALL setRuntimeException( sal_Int32 _runtimeexception ) throw(css::uno::RuntimeException); +}; + +class XLB_Invocation : public WeakImplHelper< XInvocation > +{ + Reference< XLanguageBindingTest > _xLBT; + +public: + XLB_Invocation( const Reference< XMultiServiceFactory > & /*xMgr*/, + const Reference< XLanguageBindingTest > & xLBT ) + : _xLBT( xLBT ) + {} + + // XInvocation + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() throw(css::uno::RuntimeException) + { return Reference< XIntrospectionAccess >(); } + virtual Any SAL_CALL invoke( const OUString & rFunctionName, + const Sequence< Any > & rParams, + Sequence< sal_Int16 > & rOutParamIndex, + Sequence< Any > & rOutParam ) throw(css::lang::IllegalArgumentException, css::script::CannotConvertException, css::reflection::InvocationTargetException, css::uno::RuntimeException); + virtual void SAL_CALL setValue( const OUString & rPropertyName, const Any & rValue ) throw(css::beans::UnknownPropertyException, css::script::CannotConvertException, css::reflection::InvocationTargetException, css::uno::RuntimeException); + virtual Any SAL_CALL getValue( const OUString & rPropertyName ) throw(css::beans::UnknownPropertyException, css::uno::RuntimeException); + virtual sal_Bool SAL_CALL hasMethod( const OUString & rName ) throw(css::uno::RuntimeException); + virtual sal_Bool SAL_CALL hasProperty( const OUString & rName ) throw(css::uno::RuntimeException); +}; + +Any XLB_Invocation::invoke( const OUString & rFunctionName, + const Sequence< Any > & rParams, + Sequence< sal_Int16 > & rOutParamIndex, + Sequence< Any > & rOutParam ) + throw(css::lang::IllegalArgumentException, css::script::CannotConvertException, css::reflection::InvocationTargetException, css::uno::RuntimeException) +{ + bool bImplementedMethod = true; + + Any aRet; + + OSL_ASSERT( rOutParam.getLength() == 0 ); + OSL_ASSERT( rOutParamIndex.getLength() == 0 ); + + try + { + sal_Bool aBool; + sal_Unicode aChar; + sal_Int8 nByte; + sal_Int16 nShort; + sal_uInt16 nUShort; + sal_Int32 nLong; + sal_uInt32 nULong; + sal_Int64 nHyper; + sal_uInt64 nUHyper; + float fFloat = 0.0; + double fDouble = 0.0; + TestEnum eEnum; + OUString aString; + Reference< XInterface > xInterface; + Any aAny; + Sequence< TestElement > aSeq; + TestData aData; + + if ( rFunctionName == "setValues" ) + { + OSL_ASSERT( rParams.getLength() == 17 ); + aBool = *(sal_Bool *)rParams[0].getValue(); + aChar = *(sal_Unicode *)rParams[1].getValue(); + rParams[2] >>= nByte; + rParams[3] >>= nShort; + rParams[4] >>= nUShort; + rParams[5] >>= nLong; + rParams[6] >>= nULong; + rParams[7] >>= nHyper; + rParams[8] >>= nUHyper; + rParams[9] >>= fFloat; + rParams[10] >>= fDouble; + rParams[11] >>= eEnum; + rParams[12] >>= aString; + rParams[13] >>= xInterface; + rParams[14] >>= aAny; + rParams[15] >>= aSeq; + rParams[16] >>= aData; + + _xLBT->setValues( aBool, aChar, nByte, nShort, nUShort, nLong, nULong, + nHyper, nUHyper, fFloat, fDouble, eEnum, aString, xInterface, + aAny, aSeq, aData ); + + rOutParamIndex.realloc( 0 ); + rOutParam.realloc( 0 ); + } + else if ( rFunctionName == "setValues2" ) + { + aBool = *(sal_Bool *)rParams[0].getValue(); + aChar = *(sal_Unicode *)rParams[1].getValue(); + rParams[2] >>= nByte; + rParams[3] >>= nShort; + rParams[4] >>= nUShort; + rParams[5] >>= nLong; + rParams[6] >>= nULong; + rParams[7] >>= nHyper; + rParams[8] >>= nUHyper; + rParams[9] >>= fFloat; + rParams[10] >>= fDouble; + rParams[11] >>= eEnum; + rParams[12] >>= aString; + rParams[13] >>= xInterface; + rParams[14] >>= aAny; + rParams[15] >>= aSeq; + rParams[16] >>= aData; + + aRet <<= _xLBT->setValues2( aBool, aChar, nByte, nShort, nUShort, nLong, nULong, + nHyper, nUHyper, fFloat, fDouble, eEnum, aString, xInterface, + aAny, aSeq, aData ); + + rOutParamIndex.realloc( 17 ); + rOutParamIndex[0] = 0; + rOutParamIndex[1] = 1; + rOutParamIndex[2] = 2; + rOutParamIndex[3] = 3; + rOutParamIndex[4] = 4; + rOutParamIndex[5] = 5; + rOutParamIndex[6] = 6; + rOutParamIndex[7] = 7; + rOutParamIndex[8] = 8; + rOutParamIndex[9] = 9; + rOutParamIndex[10] = 10; + rOutParamIndex[11] = 11; + rOutParamIndex[12] = 12; + rOutParamIndex[13] = 13; + rOutParamIndex[14] = 14; + rOutParamIndex[15] = 15; + rOutParamIndex[16] = 16; + + rOutParam.realloc( 17 ); + rOutParam[0].setValue( &aBool, cppu::UnoType<bool>::get() ); + rOutParam[1].setValue( &aChar, cppu::UnoType<cppu::UnoCharType>::get() ); + rOutParam[2] <<= nByte; + rOutParam[3] <<= nShort; + rOutParam[4] <<= nUShort; + rOutParam[5] <<= nLong; + rOutParam[6] <<= nULong; + rOutParam[7] <<= nHyper; + rOutParam[8] <<= nUHyper; + rOutParam[9] <<= fFloat; + rOutParam[10] <<= fDouble; + rOutParam[11] <<= eEnum; + rOutParam[12] <<= aString; + rOutParam[13] <<= xInterface; + rOutParam[14] <<= aAny; + rOutParam[15] <<= aSeq; + rOutParam[16] <<= aData; + } + else if ( rFunctionName == "getValues" ) + { + aRet <<= _xLBT->getValues( aBool, aChar, nByte, nShort, nUShort, nLong, nULong, + nHyper, nUHyper, fFloat, fDouble, eEnum, aString, xInterface, + aAny, aSeq, aData ); + + rOutParamIndex.realloc( 17 ); + rOutParamIndex[0] = 0; + rOutParamIndex[1] = 1; + rOutParamIndex[2] = 2; + rOutParamIndex[3] = 3; + rOutParamIndex[4] = 4; + rOutParamIndex[5] = 5; + rOutParamIndex[6] = 6; + rOutParamIndex[7] = 7; + rOutParamIndex[8] = 8; + rOutParamIndex[9] = 9; + rOutParamIndex[10] = 10; + rOutParamIndex[11] = 11; + rOutParamIndex[12] = 12; + rOutParamIndex[13] = 13; + rOutParamIndex[14] = 14; + rOutParamIndex[15] = 15; + rOutParamIndex[16] = 16; + + rOutParam.realloc( 17 ); + rOutParam[0].setValue( &aBool, cppu::UnoType<bool>::get() ); + rOutParam[1].setValue( &aChar, cppu::UnoType<cppu::UnoCharType>::get() ); + rOutParam[2] <<= nByte; + rOutParam[3] <<= nShort; + rOutParam[4] <<= nUShort; + rOutParam[5] <<= nLong; + rOutParam[6] <<= nULong; + rOutParam[7] <<= nHyper; + rOutParam[8] <<= nUHyper; + rOutParam[9] <<= fFloat; + rOutParam[10] <<= fDouble; + rOutParam[11] <<= eEnum; + rOutParam[12] <<= aString; + rOutParam[13] <<= xInterface; + rOutParam[14] <<= aAny; + rOutParam[15] <<= aSeq; + rOutParam[16] <<= aData; + } + else if ( rFunctionName == "raiseException" ) + { + aRet <<= _xLBT->raiseException( aBool, aChar, nByte, nShort, nUShort, nLong, nULong, + nHyper, nUHyper, fFloat, fDouble, eEnum, aString, xInterface, + aAny, aSeq, aData ); + + rOutParamIndex.realloc( 17 ); + rOutParamIndex[0] = 0; + rOutParamIndex[1] = 1; + rOutParamIndex[2] = 2; + rOutParamIndex[3] = 3; + rOutParamIndex[4] = 4; + rOutParamIndex[5] = 5; + rOutParamIndex[6] = 6; + rOutParamIndex[7] = 7; + rOutParamIndex[8] = 8; + rOutParamIndex[9] = 9; + rOutParamIndex[10] = 10; + rOutParamIndex[11] = 11; + rOutParamIndex[12] = 12; + rOutParamIndex[13] = 13; + rOutParamIndex[14] = 14; + rOutParamIndex[15] = 15; + rOutParamIndex[16] = 16; + + rOutParam.realloc( 17 ); + rOutParam[0].setValue( &aBool, cppu::UnoType<bool>::get() ); + rOutParam[1].setValue( &aChar, cppu::UnoType<cppu::UnoCharType>::get() ); + rOutParam[2] <<= nByte; + rOutParam[3] <<= nShort; + rOutParam[4] <<= nUShort; + rOutParam[5] <<= nLong; + rOutParam[6] <<= nULong; + rOutParam[7] <<= nHyper; + rOutParam[8] <<= nUHyper; + rOutParam[9] <<= fFloat; + rOutParam[10] <<= fDouble; + rOutParam[11] <<= eEnum; + rOutParam[12] <<= aString; + rOutParam[13] <<= xInterface; + rOutParam[14] <<= aAny; + rOutParam[15] <<= aSeq; + rOutParam[16] <<= aData; + } + else + { + bImplementedMethod = false; + } + } + catch (const IllegalArgumentException & rExc) + { + // thrown by raiseException() call + InvocationTargetException aExc; + aExc.TargetException <<= rExc; + throw aExc; + } + catch (Exception &) + { + OSL_FAIL( "### unexpected exception caught!" ); + throw; + } + + if (! bImplementedMethod) + { + throw IllegalArgumentException( + OUString( "not an implemented method!" ), + (OWeakObject *)this, 0 ); + } + + return aRet; +} + +void XLB_Invocation::setValue( const OUString & rName, const Any & rValue ) + throw(css::beans::UnknownPropertyException, css::script::CannotConvertException, css::reflection::InvocationTargetException, css::uno::RuntimeException) +{ + if ( rName == "Bool" ) + _xLBT->setBool( *(const sal_Bool *)rValue.getValue() ); + else if ( rName == "Byte" ) + _xLBT->setByte( *(const sal_Int8 *)rValue.getValue() ); + else if ( rName == "Char" ) + _xLBT->setChar( *(const sal_Unicode *)rValue.getValue() ); + else if ( rName == "Short" ) + _xLBT->setShort( *(const sal_Int16 *)rValue.getValue() ); + else if ( rName == "UShort" ) + _xLBT->setUShort( *(const sal_uInt16 *)rValue.getValue() ); + else if ( rName == "Long" ) + _xLBT->setLong( *(const sal_Int32 *)rValue.getValue() ); + else if ( rName == "ULong" ) + _xLBT->setULong( *(const sal_uInt32 *)rValue.getValue() ); + else if ( rName == "Hyper" ) + _xLBT->setHyper( *(const sal_Int64 *)rValue.getValue() ); + else if ( rName == "UHyper" ) + _xLBT->setUHyper( *(const sal_uInt64 *)rValue.getValue() ); + else if ( rName == "Float" ) + _xLBT->setFloat( *(const float *)rValue.getValue() ); + else if ( rName == "Double" ) + _xLBT->setDouble( *(const double *)rValue.getValue() ); + else if ( rName == "Enum" ) + _xLBT->setEnum( *(const TestEnum *)rValue.getValue() ); + else if ( rName == "String" ) + _xLBT->setString( *(const OUString *)rValue.getValue() ); + else if ( rName == "Interface" ) + _xLBT->setInterface( *(const Reference< XInterface > *)rValue.getValue() ); + else if ( rName == "Any" ) + _xLBT->setAny( rValue ); + else if ( rName == "Sequence" ) + _xLBT->setSequence( *(const Sequence< TestElement > *)rValue.getValue() ); + else if ( rName == "Struct" ) + _xLBT->setStruct( *(const TestData *)rValue.getValue() ); + else if ( rName == "RuntimeException" ) + _xLBT->setRuntimeException( *(const sal_Int32 *)rValue.getValue() ); +} + +Any XLB_Invocation::getValue( const OUString & rName ) + throw(css::beans::UnknownPropertyException, css::uno::RuntimeException) +{ + Any aRet; + if ( rName == "Bool" ) + { + sal_Bool aBool = _xLBT->getBool(); + aRet.setValue( &aBool, cppu::UnoType<bool>::get() ); + } + else if ( rName == "Byte" ) + aRet <<= _xLBT->getByte(); + else if ( rName == "Char" ) + { + sal_Unicode aChar = _xLBT->getChar(); + aRet.setValue( &aChar, cppu::UnoType<cppu::UnoCharType>::get() ); + } + else if ( rName == "Short" ) + aRet <<= _xLBT->getShort(); + else if ( rName == "UShort" ) + aRet <<= _xLBT->getUShort(); + else if ( rName == "Long" ) + aRet <<= _xLBT->getLong(); + else if ( rName == "ULong" ) + aRet <<= _xLBT->getULong(); + else if ( rName == "Hyper" ) + aRet <<= _xLBT->getHyper(); + else if ( rName == "UHyper" ) + aRet <<= _xLBT->getUHyper(); + else if ( rName == "Float" ) + aRet <<= _xLBT->getFloat(); + else if ( rName == "Double" ) + aRet <<= _xLBT->getDouble(); + else if ( rName == "Enum" ) + aRet <<= _xLBT->getEnum(); + else if ( rName == "String" ) + aRet <<= _xLBT->getString(); + else if ( rName == "Interface" ) + aRet <<= _xLBT->getInterface(); + else if ( rName == "Any" ) + aRet <<= _xLBT->getAny(); + else if ( rName == "Sequence" ) + aRet <<= _xLBT->getSequence(); + else if ( rName == "Struct" ) + aRet <<= _xLBT->getStruct(); + else if ( rName == "RuntimeException" ) + aRet <<= _xLBT->getRuntimeException(); + return aRet; +} + +sal_Bool XLB_Invocation::hasMethod( const OUString & rName ) + throw(css::uno::RuntimeException) +{ + return (rName == "raiseException" || + rName == "getValues" || + rName == "setValues2" || + rName == "setValues" || + rName == "acquire" || + rName == "release" || + rName == "queryInterface" ); +} + +sal_Bool XLB_Invocation::hasProperty( const OUString & rName ) + throw(css::uno::RuntimeException) +{ + return (rName == "Bool" || + rName == "Byte" || + rName == "Char" || + rName == "Short" || + rName == "UShort" || + rName == "Long" || + rName == "ULong" || + rName == "Hyper" || + rName == "UHyper" || + rName == "Float" || + rName == "Double" || + rName == "Enum" || + rName == "String" || + rName == "Interface" || + rName == "Any" || + rName == "Sequence" || + rName == "Struct" || + rName == "RuntimeException" ); +} + + +void Test_Impl::setValues( sal_Bool bBool, sal_Unicode cChar, sal_Int8 nByte, + sal_Int16 nShort, sal_uInt16 nUShort, + sal_Int32 nLong, sal_uInt32 nULong, + sal_Int64 nHyper, sal_uInt64 nUHyper, + float fFloat, double fDouble, + test::TestEnum eEnum, const OUString& rStr, + const css::uno::Reference< css::uno::XInterface >& xTest, + const css::uno::Any& rAny, + const css::uno::Sequence<test::TestElement >& rSequence, + const test::TestData& rStruct ) + throw(css::uno::RuntimeException) +{ + assign( _aData, + bBool, cChar, nByte, nShort, nUShort, nLong, nULong, nHyper, nUHyper, fFloat, fDouble, + eEnum, rStr, xTest, rAny, rSequence ); + _aStructData = rStruct; +} + +test::TestData Test_Impl::setValues2( sal_Bool& bBool, sal_Unicode& cChar, sal_Int8& nByte, + sal_Int16& nShort, sal_uInt16& nUShort, + sal_Int32& nLong, sal_uInt32& nULong, + sal_Int64& nHyper, sal_uInt64& nUHyper, + float& fFloat, double& fDouble, + test::TestEnum& eEnum, OUString& rStr, + css::uno::Reference< css::uno::XInterface >& xTest, + css::uno::Any& rAny, + css::uno::Sequence<test::TestElement >& rSequence, + test::TestData& rStruct ) + throw(css::uno::RuntimeException) +{ + assign( _aData, + bBool, cChar, nByte, nShort, nUShort, nLong, nULong, nHyper, nUHyper, fFloat, fDouble, + eEnum, rStr, xTest, rAny, rSequence ); + _aStructData = rStruct; + return _aStructData; +} + +test::TestData Test_Impl::getValues( sal_Bool& bBool, sal_Unicode& cChar, sal_Int8& nByte, + sal_Int16& nShort, sal_uInt16& nUShort, + sal_Int32& nLong, sal_uInt32& nULong, + sal_Int64& nHyper, sal_uInt64& nUHyper, + float& fFloat, double& fDouble, + test::TestEnum& eEnum, OUString& rStr, + css::uno::Reference< css::uno::XInterface >& xTest, + css::uno::Any& rAny, + css::uno::Sequence<test::TestElement >& rSequence, + test::TestData& rStruct ) + throw(css::uno::RuntimeException) +{ + bBool = _aData.Bool; + cChar = _aData.Char; + nByte = _aData.Byte; + nShort = _aData.Short; + nUShort = _aData.UShort; + nLong = _aData.Long; + nULong = _aData.ULong; + nHyper = _aData.Hyper; + nUHyper = _aData.UHyper; + fFloat = _aData.Float; + fDouble = _aData.Double; + eEnum = _aData.Enum; + rStr = _aData.String; + xTest = _aData.Interface; + rAny = _aData.Any; + rSequence = _aData.Sequence; + rStruct = _aStructData; + return _aStructData; +} + + +sal_Bool performTest( const Reference<XLanguageBindingTest > & xLBT ) +{ + OSL_ENSURE( xLBT.is(), "### no test interface!" ); + if (xLBT.is()) + { + // this data is never ever granted access to by calls other than equals(), assign()! + test::TestData aData; // test against this data + + Reference<XInterface > xI( *new OWeakObject() ); + + assign( (test::TestElement &)aData, + sal_True, '@', 17, 0x1234, 0xfedc, 0x12345678, 0xfedcba98, + SAL_CONST_INT64(0x123456789abcdef0), + SAL_CONST_UINT64(0xfedcba9876543210), + (float)17.03125, M_PI, TestEnum_LOLA, OUString("dumdidum"), xI, + Any( &xI, cppu::UnoType<XInterface>::get()) ); + + OSL_ENSURE( aData.Any == xI, "### unexpected any!" ); + OSL_ENSURE( !(aData.Any != xI), "### unexpected any!" ); + + aData.Sequence = Sequence<test::TestElement >( (const test::TestElement *)&aData, 1 ); + // aData complete + + + // this is a manually copy of aData for first setting... + test::TestData aSetData; + + assign( (test::TestElement &)aSetData, + aData.Bool, aData.Char, aData.Byte, aData.Short, aData.UShort, + aData.Long, aData.ULong, aData.Hyper, aData.UHyper, aData.Float, aData.Double, + aData.Enum, aData.String, xI, + Any( &xI, cppu::UnoType<XInterface>::get()) ); + + aSetData.Sequence = Sequence<test::TestElement >( (const test::TestElement *)&aSetData, 1 ); + + xLBT->setValues( + aSetData.Bool, aSetData.Char, aSetData.Byte, aSetData.Short, aSetData.UShort, + aSetData.Long, aSetData.ULong, aSetData.Hyper, aSetData.UHyper, aSetData.Float, aSetData.Double, + aSetData.Enum, aSetData.String, aSetData.Interface, aSetData.Any, aSetData.Sequence, aSetData ); + + { + test::TestData aRet, aRet2; + xLBT->getValues( + aRet.Bool, aRet.Char, aRet.Byte, aRet.Short, aRet.UShort, + aRet.Long, aRet.ULong, aRet.Hyper, aRet.UHyper, aRet.Float, aRet.Double, + aRet.Enum, aRet.String, aRet.Interface, aRet.Any, aRet.Sequence, aRet2 ); + + OSL_ASSERT( equals( aData, aRet ) && equals( aData, aRet2 ) ); + + // set last retrieved values + test::TestData aSV2ret = xLBT->setValues2( + aRet.Bool, aRet.Char, aRet.Byte, aRet.Short, aRet.UShort, + aRet.Long, aRet.ULong, aRet.Hyper, aRet.UHyper, aRet.Float, aRet.Double, + aRet.Enum, aRet.String, aRet.Interface, aRet.Any, aRet.Sequence, aRet2 ); + + OSL_ASSERT( equals( aData, aSV2ret ) && equals( aData, aRet2 ) ); + } + { + test::TestData aRet, aRet2; + test::TestData aGVret = xLBT->getValues( + aRet.Bool, aRet.Char, aRet.Byte, aRet.Short, aRet.UShort, + aRet.Long, aRet.ULong, aRet.Hyper, aRet.UHyper, aRet.Float, aRet.Double, + aRet.Enum, aRet.String, aRet.Interface, aRet.Any, aRet.Sequence, aRet2 ); + + OSL_ASSERT( equals( aData, aRet ) && equals( aData, aRet2 ) && equals( aData, aGVret ) ); + + // set last retrieved values + xLBT->setBool( aRet.Bool ); + xLBT->setChar( aRet.Char ); + xLBT->setByte( aRet.Byte ); + xLBT->setShort( aRet.Short ); + xLBT->setUShort( aRet.UShort ); + xLBT->setLong( aRet.Long ); + xLBT->setULong( aRet.ULong ); + xLBT->setHyper( aRet.Hyper ); + xLBT->setUHyper( aRet.UHyper ); + xLBT->setFloat( aRet.Float ); + xLBT->setDouble( aRet.Double ); + xLBT->setEnum( aRet.Enum ); + xLBT->setString( aRet.String ); + xLBT->setInterface( aRet.Interface ); + xLBT->setAny( aRet.Any ); + xLBT->setSequence( aRet.Sequence ); + xLBT->setStruct( aRet2 ); + } + { + test::TestData aRet, aRet2; + aRet.Hyper = xLBT->getHyper(); + aRet.UHyper = xLBT->getUHyper(); + aRet.Float = xLBT->getFloat(); + aRet.Double = xLBT->getDouble(); + aRet.Byte = xLBT->getByte(); + aRet.Char = xLBT->getChar(); + aRet.Bool = xLBT->getBool(); + aRet.Short = xLBT->getShort(); + aRet.UShort = xLBT->getUShort(); + aRet.Long = xLBT->getLong(); + aRet.ULong = xLBT->getULong(); + aRet.Enum = xLBT->getEnum(); + aRet.String = xLBT->getString(); + aRet.Interface = xLBT->getInterface(); + aRet.Any = xLBT->getAny(); + aRet.Sequence = xLBT->getSequence(); + aRet2 = xLBT->getStruct(); + + return (equals( aData, aRet ) && equals( aData, aRet2 )); + } + } + return sal_False; +} + + +test::TestData Test_Impl::raiseException( sal_Bool& /*bBool*/, sal_Unicode& /*cChar*/, sal_Int8& /*nByte*/, sal_Int16& /*nShort*/, sal_uInt16& /*nUShort*/, sal_Int32& /*nLong*/, sal_uInt32& /*nULong*/, sal_Int64& /*nHyper*/, sal_uInt64& /*nUHyper*/, float& /*fFloat*/, double& /*fDouble*/, test::TestEnum& /*eEnum*/, OUString& /*aString*/, css::uno::Reference< css::uno::XInterface >& /*xInterface*/, css::uno::Any& /*aAny*/, css::uno::Sequence< test::TestElement >& /*aSequence*/, test::TestData& /*aStruct*/ ) + throw(css::lang::IllegalArgumentException, css::uno::RuntimeException) +{ + IllegalArgumentException aExc; + aExc.ArgumentPosition = 5; + aExc.Message = "dum dum dum I dance around the circle..."; + aExc.Context = *this; + throw aExc; +} + +sal_Int32 Test_Impl::getRuntimeException() throw(css::uno::RuntimeException) +{ + RuntimeException aExc; + aExc.Message = "dum dum dum I dance around the circle..."; + aExc.Context = *this; + throw aExc; +} + +void Test_Impl::setRuntimeException( sal_Int32 /*_runtimeexception*/ ) throw(css::uno::RuntimeException) +{ + RuntimeException aExc; + aExc.Message = "dum dum dum I dance around the circle..."; + aExc.Context = *this; + throw aExc; +} + + +sal_Bool raiseException( const Reference<XLanguageBindingTest > & xLBT ) +{ + try + { + try + { + try + { + test::TestData aRet, aRet2; + xLBT->raiseException( + aRet.Bool, aRet.Char, aRet.Byte, aRet.Short, aRet.UShort, + aRet.Long, aRet.ULong, aRet.Hyper, aRet.UHyper, aRet.Float, aRet.Double, + aRet.Enum, aRet.String, aRet.Interface, aRet.Any, aRet.Sequence, aRet2 ); + return sal_False; + } + catch (const IllegalArgumentException &aExc) + { + OSL_ENSURE( aExc.ArgumentPosition == 5 && + aExc.Message.startsWith("dum dum dum I dance around the circle..."), + "### unexpected exception content!" ); + + Reference<XLanguageBindingTest > xLBT2( + Reference<XLanguageBindingTest >::query( aExc.Context ) ); + + OSL_ENSURE( xLBT2.is(), "### unexpected source of exception!" ); + if (xLBT2.is()) + xLBT2->getRuntimeException(); + else + return sal_False; + } + } + catch (const RuntimeException & rExc) + { + OSL_ENSURE( rExc.Message.startsWith("dum dum dum I dance around the circle..."), + "### unexpected exception content!" ); + + Reference<XLanguageBindingTest > xLBT2( + Reference<XLanguageBindingTest >::query( rExc.Context ) ); + + OSL_ENSURE( xLBT2.is(), "### unexpected source of exception!" ); + if (xLBT2.is()) + xLBT2->setRuntimeException( 0xcafebabe ); + else + return sal_False; + } + } + catch (const Exception & aExc) + { + OSL_ENSURE( aExc.Message.startsWith("dum dum dum I dance around the circle..."), + "### unexpected exception content!" ); + return aExc.Message.startsWith("dum dum dum I dance around the circle..."); + } + return sal_False; +} + + +static sal_Bool test_adapter( const Reference< XMultiServiceFactory > & xMgr ) +{ + Reference< XInvocationAdapterFactory > xAdapFac( + xMgr->createInstance("com.sun.star.script.InvocationAdapterFactory"), UNO_QUERY ); + Reference< XInvocationAdapterFactory2 > xAdapFac2( xAdapFac, UNO_QUERY_THROW ); + + Reference< XLanguageBindingTest > xOriginal( (XLanguageBindingTest *)new Test_Impl() ); + Reference< XInvocation > xInvok( new XLB_Invocation( xMgr, xOriginal ) ); + Reference< XLanguageBindingTest > xLBT( xAdapFac->createAdapter( + xInvok, cppu::UnoType<XLanguageBindingTest>::get()), UNO_QUERY ); + Reference< XLanguageBindingTest > xLBT2( + xAdapFac->createAdapter( + xInvok, cppu::UnoType<XLanguageBindingTest>::get()), UNO_QUERY ); + if (xLBT != xLBT2) + return sal_False; + Reference< XInterface > xLBT3( + xAdapFac->createAdapter( + xInvok, cppu::UnoType<XInterface>::get()), UNO_QUERY ); + if (xLBT != xLBT3) + return sal_False; + Type ar[ 2 ] = { + cppu::UnoType<XLBTestBase>::get(), + cppu::UnoType<XInterface>::get()}; + Reference< XInterface > xLBT4( + xAdapFac2->createAdapter( xInvok, Sequence< Type >( ar, 2 ) ), UNO_QUERY ); + if (xLBT != xLBT4) + return sal_False; + Reference< XSimpleRegistry > xInvalidAdapter( + xAdapFac->createAdapter( + xInvok, cppu::UnoType<XSimpleRegistry>::get()), UNO_QUERY ); + if (xLBT == xInvalidAdapter) + return sal_False; + + try + { + xInvalidAdapter->isValid(); + return sal_False; + } + catch (RuntimeException &) + { + } + + return (performTest( xLBT ) && raiseException( xLBT )); +} + +static sal_Bool test_invocation( const Reference< XMultiServiceFactory > & xMgr ) +{ + Reference< XInvocationAdapterFactory > xAdapFac( + xMgr->createInstance("com.sun.star.script.InvocationAdapterFactory"), UNO_QUERY ); + Reference< XSingleServiceFactory > xInvocFac( + xMgr->createInstance("com.sun.star.script.Invocation"), UNO_QUERY ); + + Reference< XLanguageBindingTest > xOriginal( (XLanguageBindingTest *)new Test_Impl() ); + Any aOriginal( &xOriginal, cppu::UnoType<decltype(xOriginal)>::get() ); + Reference< XInvocation > xInvok( + xInvocFac->createInstanceWithArguments( Sequence< Any >( &aOriginal, 1 ) ), UNO_QUERY ); + + Reference< XLanguageBindingTest > xLBT( xAdapFac->createAdapter( + xInvok, cppu::UnoType<XLanguageBindingTest>::get()), UNO_QUERY ); + + return (performTest( xLBT ) && raiseException( xLBT )); +} + +SAL_IMPLEMENT_MAIN() +{ + Reference< XMultiServiceFactory > xMgr( createRegistryServiceFactory( + OUString( "stoctest.rdb" ) ) ); + + try + { + Reference< XImplementationRegistration > xImplReg( + xMgr->createInstance( "com.sun.star.registry.ImplementationRegistration" ), + UNO_QUERY ); + OSL_ENSURE( xImplReg.is(), "### no impl reg!" ); + + xImplReg->registerImplementation( + OUString("com.sun.star.loader.SharedLibrary"), + OUString("invocadapt.uno" SAL_DLLEXTENSION), + Reference< XSimpleRegistry >() ); + xImplReg->registerImplementation( + OUString("com.sun.star.loader.SharedLibrary"), + OUString("stocservices.uno" SAL_DLLEXTENSION), + Reference< XSimpleRegistry >() ); + xImplReg->registerImplementation( + OUString("com.sun.star.loader.SharedLibrary"), + OUString("invocation.uno" SAL_DLLEXTENSION), + Reference< XSimpleRegistry >() ); + xImplReg->registerImplementation( + OUString("com.sun.star.loader.SharedLibrary"), + OUString("reflection.uno" SAL_DLLEXTENSION), + Reference< XSimpleRegistry >() ); + xImplReg->registerImplementation( + OUString("com.sun.star.loader.SharedLibrary"), + OUString("introspection.uno" SAL_DLLEXTENSION), + Reference< XSimpleRegistry >() ); + + if (test_adapter( xMgr )) + { + fprintf( stderr, "> test_iadapter() succeeded.\n" ); + if (test_invocation( xMgr )) + { + fprintf( stderr, "> test_invocation() succeeded.\n" ); + } + } + } + catch (const Exception & rExc) + { + fprintf( stderr, "> exception occurred: " ); + OString aMsg( OUStringToOString( rExc.Message, RTL_TEXTENCODING_ASCII_US ) ); + fprintf( stderr, "%s\n", aMsg.getStr() ); + } + + Reference< XComponent >( xMgr, UNO_QUERY )->dispose(); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testintrosp.cxx b/stoc/test/testintrosp.cxx new file mode 100644 index 0000000000..0b56c8564e --- /dev/null +++ b/stoc/test/testintrosp.cxx @@ -0,0 +1,1203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <sal/main.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/servicefactory.hxx> +#include <osl/diagnose.h> + +#include <ModuleA/XIntroTest.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XIntrospection.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/container/XElementAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <stdio.h> +#include <string.h> +#include <cmath> + +using namespace cppu; +using namespace ModuleA; +using namespace css::uno; +using namespace css::lang; +using namespace css::beans; +using namespace css::registry; +using namespace css::reflection; +using namespace css::container; +using namespace css::beans::PropertyAttribute; + + +#define DEFAULT_INDEX_ACCESS_COUNT 10 +#define DEFAULT_NAME_ACCESS_COUNT 5 + +// Auxiliary function, in order to get from one type XIdlClass +Reference<XIdlClass> TypeToIdlClass( const Type& rType, const Reference< XMultiServiceFactory > & xMgr ) +{ + static Reference< XIdlReflection > xRefl; + + // register void as default class + Reference<XIdlClass> xRetClass; + typelib_TypeDescription * pTD = 0; + rType.getDescription( &pTD ); + if( pTD ) + { + OUString sOWName( pTD->pTypeName ); + if( !xRefl.is() ) + { + xRefl.set( xMgr->createInstance( "com.sun.star.reflection.CoreReflection" ), UNO_QUERY ); + OSL_ENSURE( xRefl.is(), "### no corereflection!" ); + } + xRetClass = xRefl->forName( sOWName ); + } + return xRetClass; +} + + +// Helper function to convert Any to UString +// TODO: This code could be moved to a more central place. +// Currently it's used only for simple data types. + +OUString AnyToString( const Any& aValue, sal_Bool bIncludeType, const Reference< XMultiServiceFactory > & xMgr ) +{ + Type aValType = aValue.getValueType(); + TypeClass eType = aValType.getTypeClass(); + char pBuffer[50]; + + OUString aRetStr; + switch( eType ) + { + case TypeClass_TYPE: aRetStr = "TYPE TYPE"; break; + case TypeClass_INTERFACE: aRetStr = "TYPE INTERFACE"; break; + case TypeClass_SERVICE: aRetStr = "TYPE SERVICE"; break; + case TypeClass_STRUCT: aRetStr = "TYPE STRUCT"; break; + case TypeClass_TYPEDEF: aRetStr = "TYPE TYPEDEF"; break; + case TypeClass_ENUM: aRetStr = "TYPE ENUM"; break; + case TypeClass_EXCEPTION: aRetStr = "TYPE EXCEPTION"; break; + case TypeClass_SEQUENCE: aRetStr = "TYPE SEQUENCE"; break; + case TypeClass_VOID: aRetStr = "TYPE void"; break; + case TypeClass_ANY: aRetStr = "TYPE any"; break; + case TypeClass_UNKNOWN: aRetStr = "TYPE unknown"; break; + case TypeClass_BOOLEAN: + { + sal_Bool b = *(sal_Bool*)aValue.getValue(); + aRetStr = OUString::valueOf( b ); + break; + } + case TypeClass_CHAR: + { + sal_Unicode c = *(sal_Unicode*)aValue.getValue(); + aRetStr = OUString::valueOf( c ); + break; + } + case TypeClass_STRING: + { + aValue >>= aRetStr; + break; + } + case TypeClass_FLOAT: + { + float f(0.0); + aValue >>= f; + snprintf( pBuffer, sizeof( pBuffer ), "%f", f ); + aRetStr = OUString( pBuffer, strlen( pBuffer ), RTL_TEXTENCODING_ASCII_US ); + break; + } + case TypeClass_DOUBLE: + { + double d(0.0); + aValue >>= d; + snprintf( pBuffer, sizeof( pBuffer ), "%f", d ); + aRetStr = OUString( pBuffer, strlen( pBuffer ), RTL_TEXTENCODING_ASCII_US ); + break; + } + case TypeClass_BYTE: + { + sal_Int8 n(0); + aValue >>= n; + aRetStr = OUString::valueOf( (sal_Int32) n ); + break; + } + case TypeClass_SHORT: + { + sal_Int16 n(0); + aValue >>= n; + aRetStr = OUString::valueOf( (sal_Int32) n ); + break; + } + case TypeClass_LONG: + { + sal_Int32 n(0); + aValue >>= n; + aRetStr = OUString::valueOf( n ); + break; + } + default: ; + } + + if( bIncludeType ) + { + Reference< XIdlClass > xIdlClass = TypeToIdlClass( aValType, xMgr ); + aRetStr += " (Typ: " + xIdlClass->getName() + ")"; + } + return aRetStr; +} + +// XPropertySetInfo for test class + +class ImplPropertySetInfo : public WeakImplHelper< XPropertySetInfo > +{ + friend class ImplIntroTest; + + Reference< XMultiServiceFactory > mxMgr; + +public: + explicit ImplPropertySetInfo( const Reference< XMultiServiceFactory > & xMgr ) + : mxMgr( xMgr ) {} + + // Methods of XPropertySetInfo + virtual Sequence< Property > SAL_CALL getProperties( ) + throw(RuntimeException); + virtual Property SAL_CALL getPropertyByName( const OUString& aName ) + throw(UnknownPropertyException, RuntimeException); + virtual sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) + throw(RuntimeException); +}; + + +Sequence< Property > ImplPropertySetInfo::getProperties() + throw( RuntimeException ) +{ + static Sequence<Property> * pSeq = NULL; + + if( !pSeq ) + { + // Create information for the properties "Width", "Height" and "Name" + pSeq = new Sequence<Property>( 3 ); + Property * pAry = pSeq->getArray(); + + pAry[0].Name = "Factor"; + pAry[0].Handle = -1; + pAry[0].Type = cppu::UnoType<double>::get(); + pAry[0].Attributes = BOUND | TRANSIENT; + + pAry[1].Name = "MyCount"; + pAry[1].Handle = -1; + pAry[1].Type = cppu::UnoType<sal_Int32>::get(); + pAry[1].Attributes = BOUND | TRANSIENT; + + pAry[2].Name = "Info"; + pAry[2].Handle = -1; + pAry[2].Type = cppu::UnoType<OUString>::get(); + pAry[2].Attributes = TRANSIENT; + } + // Return information about all three properties + return *pSeq; +} + +Property ImplPropertySetInfo::getPropertyByName(const OUString& Name) + throw( UnknownPropertyException, RuntimeException ) +{ + Sequence<Property> aSeq = getProperties(); + const Property * pAry = aSeq.getConstArray(); + + for( sal_Int32 i = aSeq.getLength(); i--; ) + { + if( pAry[i].Name == Name ) + return pAry[i]; + } + // Property unknown, also return empty ones + return Property(); +} + +sal_Bool ImplPropertySetInfo::hasPropertyByName(const OUString& Name) + throw( RuntimeException ) +{ + Sequence<Property> aSeq = getProperties(); + const Property * pAry = aSeq.getConstArray(); + + for( sal_Int32 i = aSeq.getLength(); i--; ) + { + if( pAry[i].Name == Name ) + return sal_True; + } + // Property unknown, also return empty ones + return sal_False; +} + + +class ImplIntroTest : public WeakImplHelper< XIntroTest, XPropertySet, XNameAccess, XIndexAccess > +{ + Reference< XMultiServiceFactory > mxMgr; + + friend class ImplPropertySetInfo; + + // Properties for the PropertySet + Any aAnyArray[10]; + + Reference< XPropertySetInfo > m_xMyInfo; + + OUString m_ObjectName; + + sal_Int16 m_nMarkusAge; + sal_Int16 m_nMarkusChildrenCount; + + long m_lDroenk; + sal_Int16 m_nBla; + sal_Int16 m_nBlub; + sal_Int16 m_nGulp; + sal_Int16 m_nLaber; + TypeClass eTypeClass; + Sequence< OUString > aStringSeq; + Sequence< Sequence< Sequence< sal_Int16 > > > aMultSeq; + Reference< XIntroTest > m_xIntroTest; + + // Data for NameAccess + Reference< XIntroTest >* pNameAccessTab; + + // Data for IndexAccess + Reference< XIntroTest >* pIndexAccessTab; + sal_Int16 iIndexAccessCount; + + // struct properties + Property m_aFirstStruct; + PropertyValue m_aSecondStruct; + + // remember listener (one listener per property) + Reference< XPropertyChangeListener > aPropChangeListener; + OUString aPropChangeListenerStr; + Reference< XVetoableChangeListener > aVetoPropChangeListener; + OUString aVetoPropChangeListenerStr; + + void Init(); + +public: + explicit ImplIntroTest( const Reference< XMultiServiceFactory > & xMgr ) + : mxMgr( xMgr ) + { + Init(); + } + + // despite virtual inline, to simplify coding (testing only) + // XPropertySet + virtual Reference< XPropertySetInfo > SAL_CALL getPropertySetInfo( ) + throw(RuntimeException); + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const Any& aValue ) + throw(UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException); + virtual Any SAL_CALL getPropertyValue( const OUString& PropertyName ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException); + virtual void SAL_CALL addPropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) + {} + virtual void SAL_CALL removePropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*aListener*/ ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) + {} + virtual void SAL_CALL addVetoableChangeListener( const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener >& /*aListener*/ ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) + {} + virtual void SAL_CALL removeVetoableChangeListener( const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener >& /*aListener*/ ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) + {} + + // XIntroTest methods + // Attributes + virtual OUString SAL_CALL getObjectName() throw(RuntimeException) + { return m_ObjectName; } + virtual void SAL_CALL setObjectName( const OUString& _objectname ) throw(RuntimeException) + { m_ObjectName = _objectname; } + virtual OUString SAL_CALL getFirstName() + throw(RuntimeException); + virtual OUString SAL_CALL getLastName() throw(RuntimeException) + { return OUString("Meyer"); } + virtual sal_Int16 SAL_CALL getAge() throw(RuntimeException) + { return m_nMarkusAge; } + virtual sal_Int16 SAL_CALL getChildrenCount() throw(RuntimeException) + { return m_nMarkusChildrenCount; } + virtual void SAL_CALL setChildrenCount( sal_Int16 _childrencount ) throw(RuntimeException) + { m_nMarkusChildrenCount = _childrencount; } + virtual Property SAL_CALL getFirstStruct() throw(RuntimeException) + { return m_aFirstStruct; } + virtual void SAL_CALL setFirstStruct( const Property& _firststruct ) throw(RuntimeException) + { m_aFirstStruct = _firststruct; } + virtual PropertyValue SAL_CALL getSecondStruct() throw(RuntimeException) + { return m_aSecondStruct; } + virtual void SAL_CALL setSecondStruct( const PropertyValue& _secondstruct ) throw(RuntimeException) + { m_aSecondStruct = _secondstruct; } + + // Methods + virtual void SAL_CALL writeln( const OUString& Text ) + throw(RuntimeException); + virtual sal_Int32 SAL_CALL getDroenk( ) throw(RuntimeException) + { return m_lDroenk; } + virtual Reference< ::ModuleA::XIntroTest > SAL_CALL getIntroTest( ) throw(RuntimeException); + virtual sal_Int32 SAL_CALL getUps( sal_Int32 l ) throw(RuntimeException) + { return 2*l; } + virtual void SAL_CALL setDroenk( sal_Int32 l ) throw(RuntimeException) + { m_lDroenk = l; } + virtual sal_Int16 SAL_CALL getBla( ) throw(RuntimeException) + { return m_nBla; } + virtual void SAL_CALL setBla( sal_Int32 n ) throw(RuntimeException) + { m_nBla = (sal_Int16)n; } + virtual sal_Int16 SAL_CALL getBlub( ) throw(RuntimeException) + { return m_nBlub; } + virtual void SAL_CALL setBlub( sal_Int16 n ) throw(RuntimeException) + { m_nBlub = n; } + virtual sal_Int16 SAL_CALL getGulp( ) throw(RuntimeException) + { return m_nGulp; } + virtual sal_Int16 SAL_CALL setGulp( sal_Int16 n ) throw(RuntimeException) + { m_nGulp = n; return 1; } + virtual TypeClass SAL_CALL getTypeClass( sal_Int16 /*n*/ ) throw(RuntimeException) + { return eTypeClass; } + virtual void SAL_CALL setTypeClass( TypeClass t, double /*d1*/, double /*d2*/ ) throw(RuntimeException) + { eTypeClass = t; } + virtual Sequence< OUString > SAL_CALL getStrings( ) throw(RuntimeException) + { return aStringSeq; } + virtual void SAL_CALL setStrings( const Sequence< OUString >& Strings ) throw(RuntimeException) + { aStringSeq = Strings; } + virtual void SAL_CALL setStringsPerMethod( const Sequence< OUString >& Strings, sal_Int16 /*n*/ ) throw(RuntimeException) + { aStringSeq = Strings; } + virtual Sequence< Sequence< Sequence< sal_Int16 > > > SAL_CALL getMultiSequence( ) throw(RuntimeException) + { return aMultSeq; } + virtual void SAL_CALL setMultiSequence( const Sequence< Sequence< Sequence< sal_Int16 > > >& Seq ) throw(RuntimeException) + { aMultSeq = Seq; } + virtual void SAL_CALL addPropertiesChangeListener( const Sequence< OUString >& PropertyNames, const Reference< XPropertiesChangeListener >& Listener ) + throw(RuntimeException); + virtual void SAL_CALL removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& Listener ) + throw(RuntimeException); + + + // Methods of XElementAccess + virtual Type SAL_CALL getElementType( ) + throw(RuntimeException); + virtual sal_Bool SAL_CALL hasElements( ) + throw(RuntimeException); + + // XNameAccess methods + // Methods + virtual Any SAL_CALL getByName( const OUString& aName ) + throw(NoSuchElementException, WrappedTargetException, RuntimeException); + virtual Sequence< OUString > SAL_CALL getElementNames( ) + throw(RuntimeException); + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) + throw(RuntimeException); + + // XIndexAccess methods + // Methods + virtual sal_Int32 SAL_CALL getCount( ) + throw(RuntimeException); + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) + throw(IndexOutOfBoundsException, WrappedTargetException, RuntimeException); +}; + +void ImplIntroTest::Init() +{ + // set unique name + static sal_Int32 nObjCount = 0; + OUString aName( "IntroTest-Obj Nr. " ); + aName += OUString::valueOf( nObjCount ); + setObjectName( aName ); + + // initialize properties + aAnyArray[0] <<= 3.14; + aAnyArray[1] <<= (sal_Int32)42; + aAnyArray[2] <<= OUString( "Hallo" ); + + // fetch PropertySetInfo once for internal use + m_xMyInfo = getPropertySetInfo(); + m_xMyInfo->acquire(); // otherwise it could crash at shutdown + + m_nMarkusAge = 33; + m_nMarkusChildrenCount = 2; + + m_lDroenk = 314; + m_nBla = 42; + m_nBlub = 111; + m_nGulp = 99; + m_nLaber = 1; + eTypeClass = TypeClass_INTERFACE; + + // string sequence initialization + aStringSeq.realloc( 3 ); + aStringSeq[ 0 ] = "String 0"; + aStringSeq[ 1 ] = "String 1"; + aStringSeq[ 2 ] = "String 2"; + + // structs initialization + m_aFirstStruct.Name = "FirstStruct-Name"; + m_aFirstStruct.Handle = 77777; + //XIdlClassRef Type; + m_aFirstStruct.Attributes = -222; + + //XInterfaceRef Source; + Any Value; + Value <<= M_E; + m_aSecondStruct.Value = Value; + //XIdlClassRef ListenerType; + m_aSecondStruct.State = PropertyState_DIRECT_VALUE; + + // IndexAccess + iIndexAccessCount = DEFAULT_INDEX_ACCESS_COUNT; + pIndexAccessTab = NULL; + pNameAccessTab = NULL; +} + +Reference< XPropertySetInfo > ImplIntroTest::getPropertySetInfo() + throw(RuntimeException) +{ + static ImplPropertySetInfo aInfo( mxMgr ); + // All objects have the same Properties, so + // the Info is the same for all + return &aInfo; +} + +void ImplIntroTest::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) + throw(UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException) +{ + if( aPropChangeListener.is() && aPropertyName == aPropChangeListenerStr ) + { + PropertyChangeEvent aEvt; + aEvt.Source = (OWeakObject*)this; + aEvt.PropertyName = aPropertyName; + aEvt.PropertyHandle = 0; + aPropChangeListener->propertyChange( aEvt ); + } + if( aVetoPropChangeListener.is() && aPropertyName == aVetoPropChangeListenerStr ) + { + PropertyChangeEvent aEvt; + aEvt.Source = (OWeakObject*)this; + aEvt.PropertyName = aVetoPropChangeListenerStr; + aEvt.PropertyHandle = 0; + aVetoPropChangeListener->vetoableChange( aEvt ); + } + + Sequence<Property> aPropSeq = m_xMyInfo->getProperties(); + sal_Int32 nLen = aPropSeq.getLength(); + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + Property aProp = aPropSeq.getArray()[ i ]; + if( aProp.Name == aPropertyName ) + aAnyArray[i] = aValue; + } +} + +Any ImplIntroTest::getPropertyValue( const OUString& PropertyName ) + throw(UnknownPropertyException, WrappedTargetException, RuntimeException) +{ + Sequence<Property> aPropSeq = m_xMyInfo->getProperties(); + sal_Int32 nLen = aPropSeq.getLength(); + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + Property aProp = aPropSeq.getArray()[ i ]; + if( aProp.Name == PropertyName ) + return aAnyArray[i]; + } + return Any(); +} + +OUString ImplIntroTest::getFirstName() + throw(RuntimeException) +{ + return OUString("Markus"); +} + +void ImplIntroTest::writeln( const OUString& Text ) + throw(RuntimeException) +{ + OString aStr( Text.getStr(), Text.getLength(), RTL_TEXTENCODING_ASCII_US ); + + printf( "%s", aStr.getStr() ); +} + +Reference< XIntroTest > ImplIntroTest::getIntroTest() + throw(RuntimeException) +{ + if( !m_xIntroTest.is() ) + m_xIntroTest = new ImplIntroTest( mxMgr ); + return m_xIntroTest; +} + +// Methods of XElementAccess +Type ImplIntroTest::getElementType( ) + throw(RuntimeException) +{ + // TODO + Type aRetType; + return aRetType; +} + +sal_Bool ImplIntroTest::hasElements( ) + throw(RuntimeException) +{ + return sal_True; +} + +// XNameAccess methods +sal_Int32 getIndexForName( const OUString& ItemName ) +{ + OUString aLeftStr = ItemName.copy( 0, 4 ); + if( aLeftStr == "Item" ) + { + // TODO + OUString aNumStr = ItemName.copy( 4 ); + } + return -1; +} + + +Any ImplIntroTest::getByName( const OUString& aName ) + throw(NoSuchElementException, WrappedTargetException, RuntimeException) +{ + Any aRetAny; + + if( !pNameAccessTab ) + pNameAccessTab = new Reference< XIntroTest >[ DEFAULT_NAME_ACCESS_COUNT ]; + + sal_Int32 iIndex = getIndexForName( aName ); + if( iIndex != -1 ) + { + if( !pNameAccessTab[iIndex].is() ) + { + ImplIntroTest* p = new ImplIntroTest( mxMgr ); + OUString aName2( "IntroTest by Name-Access, Index = " ); + aName2 += OUString::valueOf( iIndex ); + p->setObjectName( aName2 ); + pNameAccessTab[iIndex] = p; + } + + Reference< XIntroTest > xRet = pNameAccessTab[iIndex]; + aRetAny = makeAny( xRet ); + } + return aRetAny; +} + +Sequence< OUString > ImplIntroTest::getElementNames( ) + throw(RuntimeException) +{ + Sequence<OUString> aStrSeq( DEFAULT_NAME_ACCESS_COUNT ); + OUString* pStr = aStrSeq.getArray(); + for( sal_Int32 i = 0 ; i < DEFAULT_NAME_ACCESS_COUNT ; i++ ) + { + OUString aName( "Item" ); + aName += OUString::valueOf( i ); + pStr[i] = aName; + } + return aStrSeq; +} + +sal_Bool ImplIntroTest::hasByName( const OUString& aName ) + throw(RuntimeException) +{ + return ( getIndexForName( aName ) != -1 ); +} + +// XIndexAccess methods +sal_Int32 ImplIntroTest::getCount( ) + throw(RuntimeException) +{ + return iIndexAccessCount; +} + +Any ImplIntroTest::getByIndex( sal_Int32 Index ) + throw(IndexOutOfBoundsException, WrappedTargetException, RuntimeException) +{ + Any aRetAny; + + if( !pIndexAccessTab ) + pIndexAccessTab = new Reference< XIntroTest >[ iIndexAccessCount ]; + + if( Index < iIndexAccessCount ) + { + if( !pNameAccessTab[Index].is() ) + { + ImplIntroTest* p = new ImplIntroTest( mxMgr ); + OUString aName( "IntroTest by Index-Access, Index = " ); + aName += OUString::valueOf( Index ); + p->setObjectName( aName ); + pIndexAccessTab[Index] = p; + } + Reference< XIntroTest > xRet = pIndexAccessTab[Index]; + aRetAny = makeAny( xRet ); + } + return aRetAny; +} + +void ImplIntroTest::addPropertiesChangeListener( const Sequence< OUString >& /*PropertyNames*/, + const Reference< XPropertiesChangeListener >& /*Listener*/ ) + throw(RuntimeException) +{ +} + +void ImplIntroTest::removePropertiesChangeListener +( const Reference< XPropertiesChangeListener >& /*Listener*/ ) + throw(RuntimeException) +{ +} + + +struct DefItem +{ + char const * pName; + sal_Int32 nConcept; +}; + +// special value for method concept, to mark "normal" functions +#define MethodConcept_NORMAL_IMPL 0x80000000 + + +// return test object +Any getIntrospectionTestObject( const Reference< XMultiServiceFactory > & xMgr ) +{ + Any aObjAny; + Reference< XIntroTest > xTestObj = new ImplIntroTest( xMgr ); + aObjAny.setValue( &xTestObj, cppu::UnoType<XIntroTest>::get()); + return aObjAny; +} + +static sal_Bool test_introsp( Reference< XMultiServiceFactory > xMgr, + Reference< XIdlReflection > /*xRefl*/, Reference< XIntrospection > xIntrospection ) +{ + DefItem pPropertyDefs[] = + { + { "Factor", PropertyConcept::PROPERTYSET }, + { "MyCount", PropertyConcept::PROPERTYSET }, + { "Info", PropertyConcept::PROPERTYSET }, + { "ObjectName", PropertyConcept::ATTRIBUTES }, + { "FirstName", PropertyConcept::ATTRIBUTES }, + { "LastName", PropertyConcept::ATTRIBUTES }, + { "Age", PropertyConcept::ATTRIBUTES }, + { "ChildrenCount", PropertyConcept::ATTRIBUTES }, + { "FirstStruct", PropertyConcept::ATTRIBUTES }, + { "SecondStruct", PropertyConcept::ATTRIBUTES }, + { "Droenk", PropertyConcept::METHODS }, + { "IntroTest", PropertyConcept::METHODS }, + { "Bla", PropertyConcept::METHODS }, + { "Blub", PropertyConcept::METHODS }, + { "Gulp", PropertyConcept::METHODS }, + { "Strings", PropertyConcept::METHODS }, + { "MultiSequence", PropertyConcept::METHODS }, + { "PropertySetInfo", PropertyConcept::METHODS }, + { "ElementType", PropertyConcept::METHODS }, + { "ElementNames", PropertyConcept::METHODS }, + { "Count", PropertyConcept::METHODS }, + { "Types", PropertyConcept::METHODS }, + { "ImplementationId", PropertyConcept::METHODS }, + { NULL, 0 } + }; + + char const * pDemandedPropVals[] = + { + "3.140000", + "42", + "Hallo", + "IntroTest-Obj Nr. 0", + "Markus", + "Meyer", + "33", + "2", + "TYPE STRUCT", + "TYPE STRUCT", + "314", + "TYPE INTERFACE", + "42", + "111", + "99", + "TYPE SEQUENCE", + "TYPE SEQUENCE", + "TYPE INTERFACE", + "TYPE TYPE", + "TYPE SEQUENCE", + "10", + "TYPE SEQUENCE", + "TYPE SEQUENCE", + }; + + char const * pDemandedModifiedPropVals[] = + { + "4.140000", + "43", + "Hallo (Modified!)", + "IntroTest-Obj Nr. 0 (Modified!)", + "Markus", + "Meyer", + "33", + "3", + "Value has not been modified", + "Value has not been modified", + "315", + "Value has not been modified", + "42", + "112", + "99", + "Value has not been modified", + "Value has not been modified", + "Value has not been modified", + "Value has not been modified", + "Value has not been modified", + "10", + "Value has not been modified", + "Value has not been modified", + }; + + char const * pDemandedPropTypes[] = + { + "double", + "long", + "string", + "string", + "string", + "string", + "short", + "short", + "com.sun.star.beans.Property", + "com.sun.star.beans.PropertyValue", + "long", + "ModuleA.XIntroTest", + "short", + "short", + "short", + "[]string", + "[][][]short", + "com.sun.star.beans.XPropertySetInfo", + "type", + "[]string", + "long", + "[]type", + "[]byte", + }; + + DefItem pMethodDefs[] = + { + { "queryInterface", MethodConcept_NORMAL_IMPL }, + { "acquire", MethodConcept::DANGEROUS }, + { "release", MethodConcept::DANGEROUS }, + { "writeln", MethodConcept_NORMAL_IMPL }, + { "getDroenk", MethodConcept::PROPERTY }, + { "getIntroTest", MethodConcept::PROPERTY }, + { "getUps", MethodConcept_NORMAL_IMPL }, + { "setDroenk", MethodConcept::PROPERTY }, + { "getBla", MethodConcept::PROPERTY }, + { "setBla", MethodConcept_NORMAL_IMPL }, + { "getBlub", MethodConcept::PROPERTY }, + { "setBlub", MethodConcept::PROPERTY }, + { "getGulp", MethodConcept::PROPERTY }, + { "setGulp", MethodConcept_NORMAL_IMPL }, + { "getTypeClass", MethodConcept_NORMAL_IMPL }, + { "setTypeClass", MethodConcept_NORMAL_IMPL }, + { "getStrings", MethodConcept::PROPERTY }, + { "setStrings", MethodConcept::PROPERTY }, + { "setStringsPerMethod", MethodConcept_NORMAL_IMPL }, + { "getMultiSequence", MethodConcept::PROPERTY }, + { "setMultiSequence", MethodConcept::PROPERTY }, + { "addPropertiesChangeListener", MethodConcept::LISTENER }, + { "removePropertiesChangeListener", MethodConcept::LISTENER }, + { "getPropertySetInfo", MethodConcept::PROPERTY }, + { "setPropertyValue", MethodConcept_NORMAL_IMPL }, + { "getPropertyValue", MethodConcept_NORMAL_IMPL }, + { "addPropertyChangeListener", MethodConcept::LISTENER }, + { "removePropertyChangeListener", MethodConcept::LISTENER }, + { "addVetoableChangeListener", MethodConcept::LISTENER }, + { "removeVetoableChangeListener", MethodConcept::LISTENER }, + { "getElementType", MethodConcept::PROPERTY | MethodConcept::NAMECONTAINER| MethodConcept::INDEXCONTAINER | MethodConcept::ENUMERATION }, + { "hasElements", MethodConcept::NAMECONTAINER | MethodConcept::INDEXCONTAINER | MethodConcept::ENUMERATION }, + { "getByName", MethodConcept::NAMECONTAINER }, + { "getElementNames", MethodConcept::PROPERTY | MethodConcept::NAMECONTAINER }, + { "hasByName", MethodConcept::NAMECONTAINER }, + { "getCount", MethodConcept::PROPERTY | MethodConcept::INDEXCONTAINER }, + { "getByIndex", MethodConcept::INDEXCONTAINER }, + { "getTypes", MethodConcept::PROPERTY }, + { "getImplementationId", MethodConcept::PROPERTY }, + { "queryAdapter", MethodConcept_NORMAL_IMPL }, + { NULL, 0 } + }; + + OString aErrorStr; + + + // create test object + Any aObjAny = getIntrospectionTestObject( xMgr ); + + // inspect introspection service + xIntrospection->inspect( aObjAny ); + xIntrospection->inspect( aObjAny ); + Reference< XIntrospectionAccess > xAccess = xIntrospection->inspect( aObjAny ); + OSL_ENSURE( xAccess.is(), "introspection failed, no XIntrospectionAccess returned" ); + if( !xAccess.is() ) + return sal_False; + + // check result of introspection + + // determine XPropertySet-UIK + Type aType = cppu::UnoType<XPropertySet>::get(); + + Reference< XInterface > xPropSetIface = xAccess->queryAdapter( aType ); + Reference< XPropertySet > xPropSet( xPropSetIface, UNO_QUERY ); + OSL_ENSURE( xPropSet.is(), "Could not get XPropertySet by queryAdapter()" ); + + // XExactName + Reference< XExactName > xExactName( xAccess, UNO_QUERY ); + OSL_ENSURE( xExactName.is(), "Introspection doesn't support ExactName" ); + + // loop over all concept combinations + for( sal_Int32 nConcepts = 0 ; nConcepts < 16 ; nConcepts++ ) + { + // how many properties should this be + sal_Int32 nDemandedPropCount = 0; + sal_Int32 iList = 0; + while( pPropertyDefs[ iList ].pName ) + { + if( pPropertyDefs[ iList ].nConcept & nConcepts ) + nDemandedPropCount++; + iList++; + } + + if( xPropSet.is() ) + { + Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + Sequence<Property> aRetSeq = xAccess->getProperties( nConcepts ); + + sal_Int32 nLen = aRetSeq.getLength(); + + aErrorStr = "Expected to find " + + OString::valueOf( nDemandedPropCount ) + + " properties but found " + + OString::valueOf( nLen ); + OSL_ENSURE( nLen == nDemandedPropCount, aErrorStr.getStr() ); + + const Property* pProps = aRetSeq.getConstArray(); + Any aPropVal; + sal_Int32 i; + iList = 0; + for( i = 0 ; i < nLen ; i++ ) + { + const Property aProp = pProps[ i ]; + + // search for next suitable method in the list + while( pPropertyDefs[ iList ].pName ) + { + if( pPropertyDefs[ iList ].nConcept & nConcepts ) + break; + iList++; + } + sal_Int32 iDemanded = iList; + iList++; + + OUString aPropName = aProp.Name; + OString aNameStr( aPropName.getStr(), aPropName.getLength(), RTL_TEXTENCODING_ASCII_US ); + + OString aDemandedName = pPropertyDefs[ iDemanded ].pName; + aErrorStr = "Expected property \"" + + aDemandedName + + "\", found \"" + + aNameStr + + "\""; + OSL_ENSURE( aNameStr == aDemandedName, aErrorStr.getStr() ); + + Type aPropType = aProp.Type; + OString aTypeNameStr( OUStringToOString(aPropType.getTypeName(), RTL_TEXTENCODING_ASCII_US) ); + OString aDemandedTypeNameStr = pDemandedPropTypes[ iDemanded ]; + aErrorStr = "Property \"" + + aDemandedName + + "\", expected type >" + + aDemandedTypeNameStr + + "< found type >" + + aTypeNameStr + + "<"; + OSL_ENSURE( aTypeNameStr == aDemandedTypeNameStr, aErrorStr.getStr() ); + + // read and report value of property + aPropVal = xPropSet->getPropertyValue( aPropName ); + + OString aValStr = OUStringToOString( AnyToString( aPropVal, sal_False, xMgr ), RTL_TEXTENCODING_ASCII_US ); + OString aDemandedValStr = pDemandedPropVals[ iDemanded ]; + aErrorStr = "Property \"" + + aDemandedName + + "\", expected val >" + + aDemandedValStr + + "< found val >" + + aValStr + + "<"; + OSL_ENSURE( aValStr == aDemandedValStr, aErrorStr.getStr() ); + + // check value and modify it according to its type + TypeClass eType = aPropVal.getValueType().getTypeClass(); + Any aNewVal; + sal_Bool bModify = sal_True; + switch( eType ) + { + case TypeClass_STRING: + { + OUString aStr; + aPropVal >>= aStr; + aStr += " (Modified!)"; + aNewVal <<= aStr; + break; + } + case TypeClass_DOUBLE: + { + double d(0.0); + aPropVal >>= d; + aNewVal <<= d + 1.0; + break; + } + case TypeClass_SHORT: + { + sal_Int16 n(0); + aPropVal >>= n; + aNewVal <<= sal_Int16( n + 1 ); + break; + } + case TypeClass_LONG: + { + sal_Int32 n(0); + aPropVal >>= n; + aNewVal <<= sal_Int32( n + 1 ); + break; + } + default: + bModify = sal_False; + break; + } + + // modify only in the last iteration + if( nConcepts == 15 ) + { + // check XExactName, switch everything to upper case + // (introspection uses lower case) + OUString aUpperUStr = aPropName.toAsciiUpperCase(); + OUString aExactName = xExactName->getExactName( aUpperUStr ); + if( aExactName != aPropName ) + { + aErrorStr = "Property \"" + + OUStringToOString( aPropName, RTL_TEXTENCODING_ASCII_US ) + + "\", not found as \"" + + OUStringToOString(aUpperUStr, RTL_TEXTENCODING_ASCII_US ) + + "\" using XExactName"; + OSL_ENSURE( sal_False, aErrorStr.getStr() ); + } + } + else + { + bModify = sal_False; + } + + // set new value, then read and return value + if( bModify ) + { + // catch UnknownPropertyException for ReadOnly properties + try + { + xPropSet->setPropertyValue( aPropName, aNewVal ); + } + catch(const UnknownPropertyException &) + { + } + + aPropVal = xPropSet->getPropertyValue( aPropName ); + + OUString aStr = AnyToString( aPropVal, sal_False, xMgr ); + OString aModifiedValStr = OUStringToOString( aStr, RTL_TEXTENCODING_ASCII_US ); + OString aDemandedModifiedValStr = pDemandedModifiedPropVals[ i ]; + aErrorStr = "Property \"" + + aDemandedName + + "\", expected modified val >" + + aDemandedModifiedValStr + + "< found val >" + + aModifiedValStr + + "<"; + OSL_ENSURE( aModifiedValStr == aDemandedModifiedValStr, aErrorStr.getStr() ); + } + + // check whether all properties can be found individually + aErrorStr = "property \"" + + aDemandedName + + "\" not found with hasProperty()"; + OUString aWDemandedName = OStringToOUString(aDemandedName, RTL_TEXTENCODING_ASCII_US ); + sal_Bool bProperty = xAccess->hasProperty( aWDemandedName, nConcepts ); + OSL_ENSURE( bProperty, aErrorStr.getStr() ); + + aErrorStr = "property \"" + + aDemandedName + + "\" not equal to same Property in sequence returned by getProperties()"; + try + { + Property aGetProp = xAccess->getProperty( aWDemandedName, nConcepts ); + } + catch (const RuntimeException &) + { + aErrorStr = "property \"" + + aDemandedName + + "\", exception was thrown when trying getProperty()"; + OSL_ENSURE( sal_False, aErrorStr.getStr() ); + } + + } + } + } + + // loop over all concept combinations + for( sal_Int32 nConcepts = 0 ; nConcepts < 128 ; nConcepts++ ) + { + // The 2^6th bit stands for "the rest" + sal_Int32 nRealConcepts = nConcepts; + if( nConcepts & 0x40 ) + nRealConcepts |= (0xFFFFFFFF - 0x3F); + + // Count the number of methods there should be + sal_Int32 nDemandedMethCount = 0; + sal_Int32 iList = 0; + while( pMethodDefs[ iList ].pName ) + { + if( pMethodDefs[ iList ].nConcept & nRealConcepts ) + nDemandedMethCount++; + iList++; + } + + // Output the method array. + Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods( nRealConcepts ); + sal_Int32 nLen = aMethodSeq.getLength(); + + aErrorStr = "Expected to find " + + OString::valueOf( nDemandedMethCount ) + + " methods but found " + + OString::valueOf( nLen ); + OSL_ENSURE( nLen == nDemandedMethCount, aErrorStr.getStr() ); + + const Reference< XIdlMethod >* pMethods = aMethodSeq.getConstArray(); + sal_Int32 i; + iList = 0; + + for( i = 0 ; i < nLen ; i++ ) + { + const Reference< XIdlMethod >& rxMethod = pMethods[i]; + + OUString aMethName = rxMethod->getName(); + OString aNameStr = OUStringToOString(aMethName, RTL_TEXTENCODING_ASCII_US ); + + // locate the next matching method in the list. + while( pMethodDefs[ iList ].pName ) + { + if( pMethodDefs[ iList ].nConcept & nRealConcepts ) + break; + iList++; + } + OString aDemandedName = pMethodDefs[ iList ].pName; + iList++; + + aErrorStr = "Expected method \"" + + aDemandedName + + "\", found \"" + + aNameStr + + "\""; + OSL_ENSURE( aNameStr == aDemandedName, aErrorStr.getStr() ); + + // Check that the method is really there with hasMethod. + aErrorStr = "method \"" + + aDemandedName + + "\" not found with hasMethod()"; + OUString aWDemandedName = OStringToOUString(aDemandedName, RTL_TEXTENCODING_ASCII_US ); + sal_Bool bMethod = xAccess->hasMethod( aWDemandedName, nRealConcepts ); + OSL_ENSURE( bMethod, aErrorStr.getStr() ); + + aErrorStr = "method \"" + + aDemandedName + + "\" not equal to same method in sequence returned by getMethods()"; + try + { + Reference< XIdlMethod > xGetMethod = xAccess->getMethod( aWDemandedName, nRealConcepts ); + OSL_ENSURE( xGetMethod == rxMethod , aErrorStr.getStr() ); + } + catch (const RuntimeException &) + { + aErrorStr = "method \"" + + aDemandedName + + "\", exception was thrown when trying getMethod()"; + OSL_ENSURE( sal_False, aErrorStr.getStr() ); + } + } + } + + // print listener class + Sequence< Type > aClassSeq = xAccess->getSupportedListeners(); + sal_Int32 nLen = aClassSeq.getLength(); + + const Type* pListeners = aClassSeq.getConstArray(); + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + const Type& aListenerType = pListeners[i]; + OUString aListenerClassName = aListenerType.getTypeName(); + } + + return sal_True; +} + + +SAL_IMPLEMENT_MAIN() +{ + Reference< XMultiServiceFactory > xMgr( createRegistryServiceFactory( OUString("stoctest.rdb") ) ); + + sal_Bool bSucc = sal_False; + try + { + Reference< XImplementationRegistration > xImplReg( + xMgr->createInstance("com.sun.star.registry.ImplementationRegistration"), UNO_QUERY ); + OSL_ENSURE( xImplReg.is(), "### no impl reg!" ); + + // Register services + OUString libName( "reflection.uno" SAL_DLLEXTENSION ); + fprintf(stderr, "1\n" ); + xImplReg->registerImplementation(OUString("com.sun.star.loader.SharedLibrary"), + libName, Reference< XSimpleRegistry >() ); + fprintf(stderr, "2\n" ); + Reference< XIdlReflection > xRefl( xMgr->createInstance("com.sun.star.reflection.CoreReflection"), UNO_QUERY ); + OSL_ENSURE( xRefl.is(), "### no corereflection!" ); + + // Introspection + libName = OUString( "introspection.uno" SAL_DLLEXTENSION); + fprintf(stderr, "3\n" ); + xImplReg->registerImplementation(OUString("com.sun.star.loader.SharedLibrary"), + libName, Reference< XSimpleRegistry >() ); + fprintf(stderr, "4\n" ); + Reference< XIntrospection > xIntrosp( xMgr->createInstance("com.sun.star.beans.Introspection"), UNO_QUERY ); + OSL_ENSURE( xRefl.is(), "### no corereflection!" ); + + fprintf(stderr, "before test_introsp\n" ); + bSucc = test_introsp( xMgr, xRefl, xIntrosp ); + fprintf(stderr, "after test_introsp\n" ); + } + catch (const Exception & rExc) + { + DBG_UNHANDLED_EXCEPTION("stoc", "### exception occurred: " << rExc ); + } + + Reference< XComponent >( xMgr, UNO_QUERY )->dispose(); + + printf( "testintrosp %s !\n", (bSucc ? "succeeded" : "failed") ); + return (bSucc ? 0 : -1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testintrosp.idl b/stoc/test/testintrosp.idl new file mode 100644 index 0000000000..fbb53ffb7e --- /dev/null +++ b/stoc/test/testintrosp.idl @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module ModuleA +{ +//module com { module sun { module star { module beans { + +//interface XPropertyChangeListener; +//interface XPropertiesChangeListener; + + +/** This is a test interface for introspection. + + <p>IMPORTANT: FOR TEST ONLY! + + <p>The following interface is just for testing purposes. It will not + stay in the product. It is only used as an exportable test class, + i.e. for BASIC integration. + + @deprecated +*/ +interface XIntroTest: com::sun::star::uno::XInterface +{ + + /** contains the ID-String of the implementation. + */ + [attribute] string ObjectName; + + + /** contains the first name of a person. + */ + [readonly, attribute] string FirstName; + + + /** contains the last name of a person. + */ + [readonly, attribute] string LastName; + + + /** contains the age of a person. + */ + [readonly, attribute] short Age; + + + /** contains the number of children person has. + */ + [attribute] short ChildrenCount; + + + /** contains a struct of type Property. + */ + [attribute] com::sun::star::beans::Property FirstStruct; + + + /** contains a struct of type PropertyValue. + */ + [attribute] com::sun::star::beans::PropertyValue SecondStruct; + + + /** output method + */ + void writeln( [in] string Text ); + + + /** ... + */ + long getDroenk(); + + + /** get further introspection test objects + */ + XIntroTest getIntroTest(); + //com::sun::star::beans::XIntroTest getIntroTest(); + + + /** !!! No property, because parameter exists + */ + long getUps( [in] long l ); + + + /** ... + */ + void setDroenk( [in] long l ); + + + /** ... + */ + short getBla(); + + + /** !!! Not the set method for property Bla, because param type != return type. + */ + void setBla( [in] long n ); + + + /** ... + */ + short getBlub(); + + + /** ... + */ + void setBlub( [in] short n ); + + + /** ... + */ + short getGulp(); + + + /** !!! Not the set method for property Gulp, because return type != void. + */ + short setGulp( [in] short n ); + + + /** ... + */ + com::sun::star::uno::TypeClass getTypeClass( [in] short n ); + + + /** ... + */ + void setTypeClass( [in] com::sun::star::uno::TypeClass t, + [in] double d1, + [in] double d2 ); + + + /** + */ + sequence<string> getStrings(); + + + /** ... + */ + void setStrings( [in] sequence<string> Strings ); + + + /** ... + */ + void setStringsPerMethod( [in] sequence<string> Strings, + [in] short n ); + + + /** + */ + sequence< sequence< sequence< short > > > getMultiSequence(); + + + /** ... + */ + void setMultiSequence( [in] sequence< sequence< sequence< short > > > Seq ); + + + /**Add a PropertiesChangeListener + */ + void addPropertiesChangeListener( [in] sequence< string > PropertyNames, + [in] com::sun::star::beans::XPropertiesChangeListener Listener ); + + + /**Remove a PropertiesChangeListener + */ + void removePropertiesChangeListener( [in] com::sun::star::beans::XPropertiesChangeListener Listener ); + +}; + + + +}; +//}; }; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testloader.cxx b/stoc/test/testloader.cxx new file mode 100644 index 0000000000..beb77e7d96 --- /dev/null +++ b/stoc/test/testloader.cxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <stdio.h> + +#include <sal/main.h> +#include <osl/module.hxx> +#include <osl/diagnose.h> + +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/factory.hxx> + +#if defined ( UNX ) +#include <limits.h> +#define _MAX_PATH PATH_MAX +#endif + +using namespace css::uno; +using namespace css::loader; +using namespace css::lang; +using namespace osl; +using namespace cppu; + + +class EmptyComponentContext : public WeakImplHelper< XComponentContext > +{ +public: + virtual Any SAL_CALL getValueByName( const OUString& /*Name*/ ) + throw (RuntimeException) + { + return Any(); + } + virtual Reference< XMultiComponentFactory > SAL_CALL getServiceManager( ) + throw (RuntimeException) + { + return Reference< XMultiComponentFactory > (); + } + +}; + + +SAL_IMPLEMENT_MAIN() +{ + Reference<XInterface> xIFace; + + Module module; + + OUString dllName( + "bootstrap.uno" SAL_DLLEXTENSION ); + + if (module.load(dllName)) + { + // try to get provider from module + component_getFactoryFunc pCompFactoryFunc = (component_getFactoryFunc) + module.getFunctionSymbol( OUString(COMPONENT_GETFACTORY) ); + + if (pCompFactoryFunc) + { + XSingleServiceFactory * pRet = (XSingleServiceFactory *)(*pCompFactoryFunc)( + "com.sun.star.comp.stoc.DLLComponentLoader", 0, 0 ); + if (pRet) + { + xIFace = pRet; + pRet->release(); + } + } + } + + OSL_ENSURE( xIFace.is(), "testloader error1"); + + Reference<XSingleComponentFactory> xFactory( Reference<XSingleComponentFactory>::query(xIFace) ); + + OSL_ENSURE( xFactory.is(), "testloader error2"); + + Reference<XInterface> xLoader = xFactory->createInstanceWithContext( new EmptyComponentContext ); + + OSL_ENSURE( xLoader.is(), "testloader error3"); + + Reference<XServiceInfo> xServInfo( Reference<XServiceInfo>::query(xLoader) ); + + OSL_ENSURE( xServInfo.is(), "testloader error4"); + + OSL_ENSURE( xServInfo->getImplementationName() == "com.sun.star.comp.stoc.DLLComponentLoader", "testloader error5"); + OSL_ENSURE( xServInfo->supportsService("com.sun.star.loader.SharedLibrary"), "testloader error6"); + OSL_ENSURE( xServInfo->getSupportedServiceNames().getLength() == 1, "testloader error7"); + + xIFace.clear(); + xFactory.clear(); + xLoader.clear(); + xServInfo.clear(); + + printf("Test Dll ComponentLoader, OK!\n"); + + return 0; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testproxyfac.cxx b/stoc/test/testproxyfac.cxx new file mode 100644 index 0000000000..befc67ae8e --- /dev/null +++ b/stoc/test/testproxyfac.cxx @@ -0,0 +1,359 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <sal/main.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <rtl/alloc.h> +#include <uno/environment.hxx> +#include <uno/lbnames.h> +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/reflection/XProxyFactory.hpp> + +#include <stdio.h> + + +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; + + +typedef WeakImplHelper< lang::XServiceInfo, + XCurrentContext, + reflection::XProxyFactory > t_impl; + + +class TargetObject : public t_impl +{ +public: + static int s_obj; + + virtual ~TargetObject() { + --s_obj; + SAL_INFO("stoc", "~TargetObject()" ); + } + TargetObject() + { ++s_obj; } + + Any SAL_CALL queryInterface( Type const & type ) + throw (RuntimeException); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() throw (RuntimeException) + { return OUString("target"); } + virtual sal_Bool SAL_CALL supportsService( const OUString & /*rServiceName*/ ) + throw (RuntimeException) + { return sal_False; } + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() + throw (RuntimeException) + { return Sequence< OUString >(); } + // XProxyFactory + virtual Reference< XAggregation > SAL_CALL createProxy( + const Reference< XInterface > & xTarget ) throw (RuntimeException) + { return Reference< XAggregation >( xTarget, UNO_QUERY ); } + // XCurrentContext + virtual Any SAL_CALL getValueByName( OUString const & name ) + throw (RuntimeException) + { return makeAny( name ); } +}; + + +Any TargetObject::queryInterface( Type const & type ) + throw (RuntimeException) +{ + Any ret( t_impl::queryInterface( type ) ); + if (ret.hasValue()) + return ret; + throw lang::DisposedException( + OUString( "my test exception" ), + getXWeak() ); +} + +int TargetObject::s_obj = 0; + + +class TestMaster : public WeakImplHelper< lang::XServiceInfo > +{ + Reference< XAggregation > m_xProxyTarget; + Reference<lang::XServiceInfo> m_xOtherProxyTargetBeforeSetDelegator; + + inline TestMaster() { ++s_obj; } +public: + static int s_obj; + static Reference< XInterface > create( + Reference< reflection::XProxyFactory > const & xProxyFac ); + static Reference< XInterface > create( + Reference< XInterface > const & xTarget, + Reference< reflection::XProxyFactory > const & xProxyFac ); + + virtual ~TestMaster() { + --s_obj; + SAL_INFO("stoc", "~TestMaster()" ); + } + + virtual Any SAL_CALL queryInterface( const Type & rType ) + throw (RuntimeException) + { + Any aRet( + WeakImplHelper< lang::XServiceInfo >::queryInterface( rType ) ); + if (aRet.hasValue()) + return aRet; + return m_xProxyTarget->queryAggregation( rType ); + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() throw (RuntimeException) + { return OUString("master"); } + virtual sal_Bool SAL_CALL supportsService( const OUString & /*rServiceName*/ ) + throw (RuntimeException) + { return sal_False; } + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() + throw (RuntimeException) + { return Sequence< OUString >(); } +}; + +int TestMaster::s_obj = 0; + + +Reference< XInterface > TestMaster::create( + Reference< XInterface > const & xTarget, + Reference< reflection::XProxyFactory > const & xProxyFac ) +{ + TestMaster * that = new TestMaster; + Reference< XInterface > xRet( getXWeak( that ) ); + { + Reference< XAggregation > xAgg( xProxyFac->createProxy( xTarget ) ); + // ownership take over + that->m_xProxyTarget.set( xAgg, UNO_QUERY_THROW ); + that->m_xOtherProxyTargetBeforeSetDelegator.set( + that->m_xProxyTarget, UNO_QUERY ); + } + that->m_xProxyTarget->setDelegator( xRet ); + return xRet; +} + +Reference< XInterface > TestMaster::create( + Reference< reflection::XProxyFactory > const & xProxyFac ) +{ + return create( + getXWeak( new TargetObject ), xProxyFac ); +} + + +static void test_proxyfac_( + Reference< XInterface > const & xMaster, OUString const & test, + Reference< reflection::XProxyFactory > const & /*xProxyFac*/ ) +{ + (void)test; + Reference< lang::XServiceInfo > xMaster_XServiceInfo( + xMaster, UNO_QUERY_THROW ); + OSL_ASSERT( xMaster_XServiceInfo->getImplementationName().equals( test ) ); + + Reference< reflection::XProxyFactory > xTarget( xMaster, UNO_QUERY_THROW ); + Reference< XCurrentContext > xTarget_XCurrentContext( + xTarget, UNO_QUERY_THROW ); + Reference< XCurrentContext > xMaster_XCurrentContext( + xMaster, UNO_QUERY_THROW ); + + OSL_ASSERT( + xTarget_XCurrentContext->getValueByName( test ) == makeAny( test ) ); + OSL_ASSERT( + xMaster_XCurrentContext->getValueByName( test ) == makeAny( test ) ); + + Reference< XAggregation > xFakeAgg( xTarget->createProxy( xTarget ) ); + if (xFakeAgg.is()) + { + OSL_ASSERT( xTarget == xFakeAgg ); + OSL_ASSERT( xMaster == xFakeAgg ); + } + + Reference< lang::XServiceInfo > xTarget_XServiceInfo( + xTarget, UNO_QUERY_THROW ); + OSL_ASSERT( xTarget_XServiceInfo->getImplementationName().equals( test ) ); + Reference< lang::XServiceInfo > xTarget_XServiceInfo2( + xTarget, UNO_QUERY_THROW ); + OSL_ASSERT( xTarget_XServiceInfo2.get() == xTarget_XServiceInfo.get() ); + + OSL_ASSERT( xTarget == xTarget_XCurrentContext ); + OSL_ASSERT( xTarget_XCurrentContext == xMaster ); + OSL_ASSERT( + xTarget_XCurrentContext.get() == xMaster_XCurrentContext.get() ); + OSL_ASSERT( xTarget_XCurrentContext == xMaster ); + OSL_ASSERT( xTarget == xMaster ); + OSL_ASSERT( xTarget_XServiceInfo.get() == xMaster_XServiceInfo.get() ); + OSL_ASSERT( xTarget_XServiceInfo == xMaster ); + OSL_ASSERT( xMaster_XServiceInfo == xMaster ); + + try + { + Reference< registry::XRegistryKey >( + xMaster, UNO_QUERY_THROW ); + } + catch (const lang::DisposedException & exc) + { + if ( exc.Message != "my test exception" ) + throw; + } +} + +static void test_proxyfac( + Reference< XInterface > const & xMaster, OUString const & test, + Reference< reflection::XProxyFactory > const & xProxyFac ) +{ + test_proxyfac_( xMaster, test, xProxyFac ); + // proxy the proxy... + Reference< XInterface > xNew( TestMaster::create( xMaster, xProxyFac ) ); + test_proxyfac_( + xNew, OUString( "master" ), xProxyFac ); +} + +SAL_IMPLEMENT_MAIN() +{ + bool success = true; + + Environment cpp_env; + OUString cpp( CPPU_CURRENT_LANGUAGE_BINDING_NAME ); + uno_getEnvironment( + reinterpret_cast< uno_Environment ** >( &cpp_env ), + cpp.pData, 0 ); + OSL_ENSURE( cpp_env.is(), "### cannot get C++ uno env!" ); + + { + Reference< lang::XMultiServiceFactory > xMgr( + createRegistryServiceFactory( + OUString( "stoctest.rdb" ) ) ); + + try + { + Reference< registry::XImplementationRegistration > xImplReg( + xMgr->createInstance( "com.sun.star.registry.ImplementationRegistration" ), + UNO_QUERY ); + OSL_ENSURE( xImplReg.is(), "### no impl reg!" ); + + OUString aLibName( + "proxyfac.uno" SAL_DLLEXTENSION ); + xImplReg->registerImplementation( + OUString( "com.sun.star.loader.SharedLibrary" ), + aLibName, Reference< registry::XSimpleRegistry >() ); + + Reference< reflection::XProxyFactory > xProxyFac( + xMgr->createInstance("com.sun.star.reflection.ProxyFactory"), + UNO_QUERY_THROW ); + + Reference< XAggregation > x( + xProxyFac->createProxy( + getXWeak( new TargetObject ) ) ); + // no call + + { + Reference< XInterface > xMaster( TestMaster::create( xProxyFac ) ); + test_proxyfac( + xMaster, + OUString( "master" ), + xProxyFac ); + } + { + Reference< XInterface > xMaster( TestMaster::create( xProxyFac ) ); + // no call + } + + { + Reference< XInterface > xMaster( TestMaster::create( xProxyFac ) ); + Reference< reflection::XProxyFactory > xSlave_lives_alone( + xMaster, UNO_QUERY_THROW ); + xMaster.clear(); + test_proxyfac( + xSlave_lives_alone, + OUString( "master" ), + xProxyFac ); + uno_dumpEnvironment( stdout, cpp_env.get(), 0 ); + } + { + Reference< XInterface > xMaster( TestMaster::create( xProxyFac ) ); + Reference< reflection::XProxyFactory > xSlave_lives_alone( + xMaster, UNO_QUERY_THROW ); + // no call + } + + test_proxyfac( + xProxyFac->createProxy( + getXWeak( new TargetObject ) ), + OUString( "target" ), + xProxyFac ); + uno_dumpEnvironment( stdout, cpp_env.get(), 0 ); + } + catch (const Exception & rExc) + { + (void)rExc; + OSL_ENSURE( + ! __FILE__, + OUStringToOString( + rExc.Message, RTL_TEXTENCODING_ASCII_US ).getStr() ); + success = false; + } + + + Reference< lang::XComponent > xComp; + Reference< beans::XPropertySet >( + xMgr, UNO_QUERY_THROW )->getPropertyValue( + OUString( "DefaultContext" ) ) + >>= xComp; + xComp->dispose(); + } + + if (TestMaster::s_obj != 0) + fprintf( stderr, "TestMaster objects: %d\n", TestMaster::s_obj ); + if (TargetObject::s_obj != 0) + fprintf( stderr, "TargetObject objects: %d\n", TargetObject::s_obj ); + + uno_dumpEnvironment( stdout, cpp_env.get(), 0 ); + void ** ppInterfaces; + sal_Int32 len; + uno_ExtEnvironment * env = cpp_env.get()->pExtEnv; + (*env->getRegisteredInterfaces)( + env, &ppInterfaces, &len, rtl_allocateMemory ); + if (len != 0) + fprintf( stderr, "%d registered C++ interfaces left!\n", len ); + + success &= (TestMaster::s_obj == 0 && + TargetObject::s_obj == 0 && + len == 0); + if (success) + { + printf( "testproxyfac succeeded.\n" ); + return 0; + } + else + { + fprintf( stderr, "testproxyfac failed!\n" ); + return 1; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testregistry.cxx b/stoc/test/testregistry.cxx new file mode 100644 index 0000000000..ef0d0a91d5 --- /dev/null +++ b/stoc/test/testregistry.cxx @@ -0,0 +1,670 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sal/main.h> +#include <osl/module.hxx> +#include <osl/diagnose.h> +#include <osl/process.h> +#include <registry/registry.hxx> + + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/bootstrap.hxx> +#include <cppuhelper/servicefactory.hxx> + +#include <com/sun/star/lang/XComponent.hpp> + +#if defined ( UNX ) +#include <limits.h> +#define _MAX_PATH PATH_MAX +#endif + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::registry; +using namespace css::lang; +using namespace css::beans; +using namespace osl; + + +namespace stoc_impreg +{ +void SAL_CALL mergeKeys( +Reference< registry::XRegistryKey > const & xDest, +Reference< registry::XRegistryKey > const & xSource ); +} +static void mergeKeys( +Reference< registry::XSimpleRegistry > const & xDest, +OUString const & rBaseNode, +OUString const & rURL ) +{ +Reference< registry::XRegistryKey > xDestRoot( xDest->getRootKey() ); +Reference< registry::XRegistryKey > xDestKey; +if (rBaseNode.getLength()) +{ +xDestKey = xDestRoot->createKey( rBaseNode ); +xDestRoot->closeKey(); +} +else +{ +xDestKey = xDestRoot; +} +Reference< registry::XSimpleRegistry > xSimReg( ::cppu::createSimpleRegistry() ); +xSimReg->open( rURL, sal_True, sal_False ); +OSL_ASSERT( xSimReg->isValid() ); +Reference< registry::XRegistryKey > xSourceKey( xSimReg->getRootKey() ); +::stoc_impreg::mergeKeys( xDestKey, xSourceKey ); +xSourceKey->closeKey(); +xSimReg->close(); +xDestKey->closeKey(); +} + + +OString userRegEnv("STAR_USER_REGISTRY="); + +OUString getExePath() +{ +OUString exe; +OSL_VERIFY( osl_getExecutableFile( &exe.pData ) == osl_Process_E_None); +#if defined(_WIN32) +exe = exe.copy(0, exe.getLength() - 16); +#else +exe = exe.copy(0, exe.getLength() - 12); +#endif +return exe; +} + +void setStarUserRegistry() +{ +Registry *myRegistry = new Registry(); + +RegistryKey rootKey, rKey, rKey2; + +OUString userReg = getExePath(); +userReg += "user.rdb"; +if(myRegistry->open(userReg, RegAccessMode::READWRITE)) +{ +OSL_VERIFY(!myRegistry->create(userReg)); +} + +OSL_VERIFY(!myRegistry->close()); +delete myRegistry; + +userRegEnv += OUStringToOString(userReg, RTL_TEXTENCODING_ASCII_US); + putenv((char *)userRegEnv.getStr()); +} + +void setLinkInDefaultRegistry(const OUString& linkName, const OUString& linkTarget) +{ + Registry *myRegistry = new Registry(); + + RegistryKey rootKey; + + OUString appReg = getExePath(); + appReg += "stoctest.rdb"; + + OSL_VERIFY(!myRegistry->open(appReg, RegAccessMode::READWRITE)); + OSL_VERIFY(!myRegistry->openRootKey(rootKey)); + + OSL_VERIFY(!rootKey.createLink(linkName, linkTarget)); + + OSL_VERIFY(!rootKey.closeKey()); + OSL_VERIFY(!myRegistry->close()); + + delete myRegistry; +} + + +void test_SimpleRegistry( + OUString const & testreg, + OUString const & testreg2, + bool bMergeDifferently = true ) +{ + Reference<XInterface> xIFace; + Module module; + + OUString dllName( + "simplereg.uno" SAL_DLLEXTENSION ); + + if (module.load(dllName)) + { + // try to get provider from module + component_getFactoryFunc pCompFactoryFunc = (component_getFactoryFunc) + module.getFunctionSymbol( OUString(COMPONENT_GETFACTORY) ); + + if (pCompFactoryFunc) + { + XSingleServiceFactory * pRet = (XSingleServiceFactory *) + (*pCompFactoryFunc)( + "com.sun.star.comp.stoc.SimpleRegistry", 0, 0 ); + if (pRet) + { + xIFace = pRet; + pRet->release(); + } + } + } + + OSL_ENSURE( xIFace.is(), "test_SimpleRegistry error1"); + + Reference<XSingleServiceFactory> xFactory( Reference<XSingleServiceFactory>::query(xIFace) ); + xIFace.clear(); + + OSL_ENSURE( xFactory.is(), "testloader error11"); + + Reference<XInterface> xIFace2 = xFactory->createInstance(); + xFactory.clear(); + + OSL_ENSURE( xIFace2.is(), "testloader error12"); + + Reference<XServiceInfo> xServInfo( Reference<XServiceInfo>::query(xIFace2) ); + + OSL_ENSURE( xServInfo.is(), "test_SimpleRegistry error2"); + + OSL_ENSURE( xServInfo->getImplementationName() == "com.sun.star.comp.stoc.SimpleRegistry", "test_SimpleRegistry error3"); + OSL_ENSURE( xServInfo->supportsService("com.sun.star.registry.SimpleRegistry"), "test_SimpleRegistry error4"); + OSL_ENSURE( xServInfo->getSupportedServiceNames().getLength() == 1, "test_SimpleRegistry error5"); + xServInfo.clear(); + + Reference<XSimpleRegistry> xReg( Reference<XSimpleRegistry>::query(xIFace2) ); + xIFace2.clear(); + + OSL_ENSURE( xReg.is(), "test_SimpleRegistry error6"); + + try + { + xReg->open(testreg, sal_False, sal_True); + + OSL_ENSURE( xReg->isValid() != sal_False, "test_SimpleRegistry error 7" ); + OSL_ENSURE( xReg->isReadOnly() == sal_False, "test_SimpleRegistry error 8" ); + + Reference<XRegistryKey> xRootKey(xReg->getRootKey()); + OSL_ENSURE( xRootKey->isValid(), "test_SimpleRegistry error 9" ); + + Reference<XRegistryKey> xKey = xRootKey->createKey(OUString( "FirstKey" )); + + Reference<XRegistryKey> xSubKey = xKey->createKey(OUString( "FirstSubKey" )); + xSubKey->setLongValue(123456789); + + xSubKey = xKey->createKey(OUString( "SecondSubKey" )); + xSubKey->setAsciiValue(OUString( "I'm an ascii value" )); + + xSubKey = xKey->createKey(OUString( "ThirdSubKey" )); + xSubKey->setStringValue(OUString( "I'm a Unicode value" )); + + xSubKey = xKey->createKey(OUString( "FourthSubKey" )); + Sequence<sal_Int8> aSeq((sal_Int8*)"I'm a binary value", 25); + xSubKey->setBinaryValue(aSeq); + + Sequence<OUString> seqNames = xKey->getKeyNames(); + Sequence< Reference<XRegistryKey> > seqKeys = xKey->openKeys(); + + OUString name; + for (sal_Int32 i=0; i < seqNames.getLength(); i++) + { + name = seqNames.getArray()[i]; + xSubKey = seqKeys.getArray()[i]; + + if (name == "/FirstKey/FirstSubKey" ) + { + OSL_ENSURE( xSubKey->getLongValue() == 123456789, + "test_SimpleRegistry error 10" ); + } else + if (name == "/FirstKey/SecondSubKey" ) + { + OSL_ENSURE( xSubKey->getAsciiValue() == "I'm an ascii value", + "test_SimpleRegistry error 11" ); + } else + if (name == "/FirstKey/ThirdSubKey" ) + { + OSL_ENSURE( xSubKey->getStringValue() == "I'm a Unicode value", + "test_SimpleRegistry error 12" ); + } else + if (name == "/FirstKey/FourthSubKey" ) + { + Sequence<sal_Int8> seqByte = xSubKey->getBinaryValue(); + OSL_ENSURE(!strcmp(((const char*)seqByte.getArray()), "I'm a binary value"), + "test_SimpleRegistry error 13" ); + } + + seqKeys.getArray()[i]->closeKey(); + } + + xKey->closeKey(); + + xRootKey->deleteKey(OUString( "FirstKey" )); + xRootKey->createKey(OUString( "SecondFirstKey" )); + + xKey = xRootKey->createKey(OUString( "SecondKey" )); + sal_Int32 pLongs[3] = {123, 456, 789}; + Sequence<sal_Int32> seqLongs(pLongs, 3); + xKey->setLongListValue(seqLongs); + + Sequence<sal_Int32> seqLongs2; + seqLongs2 = xKey->getLongListValue(); + OSL_ENSURE( seqLongs.getLength() == 3, "test_SimpleRegistry error 14" ); + OSL_ENSURE( seqLongs.getArray()[0] == 123, "test_SimpleRegistry error 15" ); + OSL_ENSURE( seqLongs.getArray()[1] == 456, "test_SimpleRegistry error 16" ); + OSL_ENSURE( seqLongs.getArray()[2] == 789, "test_SimpleRegistry error 17" ); + + + xKey = xRootKey->createKey(OUString( "ThirdKey" )); + OUString pAscii[3]; + pAscii[0] = "Hello"; + pAscii[1] = "here I"; + pAscii[2] = "come"; + + Sequence<OUString> seqAscii(pAscii, 3); + xKey->setAsciiListValue(seqAscii); + + Sequence<OUString> seqAscii2; + seqAscii2 = xKey->getAsciiListValue(); + OSL_ENSURE( seqAscii2.getLength() == 3, "test_SimpleRegistry error 18" ); + OSL_ENSURE( seqAscii2.getArray()[0] == "Hello", "test_SimpleRegistry error 19"); + OSL_ENSURE( seqAscii2.getArray()[1] == "here I", "test_SimpleRegistry error 20"); + OSL_ENSURE( seqAscii2.getArray()[2] == "come", "test_SimpleRegistry error 21"); + + xKey = xRootKey->createKey(OUString( "FourthKey" )); + OUString pUnicode[3]; + pUnicode[0] = "Hello"; + pUnicode[1] = "here I"; + pUnicode[2] = "come as unicode"; + + Sequence<OUString> seqUnicode(pUnicode, 3); + xKey->setStringListValue(seqUnicode); + + Sequence<OUString> seqUnicode2; + seqUnicode2 = xKey->getStringListValue(); + OSL_ENSURE( seqUnicode2.getLength() == 3, "test_SimpleRegistry error 22" ); + OSL_ENSURE( seqUnicode2.getArray()[0] == "Hello", "test_SimpleRegistry error 23"); + OSL_ENSURE( seqUnicode2.getArray()[1] == "here I", "test_SimpleRegistry error 24"); + OSL_ENSURE( seqUnicode2.getArray()[2] == "come as unicode", "test_SimpleRegistry error 25"); + + + xReg->open(testreg2, sal_False, sal_True); + OSL_ENSURE( xReg->isValid() != sal_False, "test_SimpleRegistry error 25" ); + xRootKey = xReg->getRootKey(); + xKey = xRootKey->createKey(OUString( "ThirdKey/FirstSubKey/WithSubSubKey" )); + xKey->closeKey(); + OSL_VERIFY( + xRootKey->createLink( + OUString( "LinkTest" ), + OUString( "/ThirdKey/FirstSubKey/WithSubSubKey" )) ); + xRootKey->closeKey(); + xReg->close(); + + xReg->open(testreg, sal_False, sal_False); + OSL_ENSURE( xReg->isValid() != sal_False, "test_SimpleRegistry error 26" ); + + if (bMergeDifferently) + { + mergeKeys( + xReg, + OUString(), + testreg2 ); + } + else + { + xReg->mergeKey(OUString(), testreg2); + } + + xRootKey = xReg->getRootKey(); + xKey = xRootKey->openKey("LinkTest"); + OSL_ENSURE( xKey.is() && xKey->isValid() && xKey->getKeyName() == "/ThirdKey/FirstSubKey/WithSubSubKey", "test_SimpleRegistry error 1213" ); + xKey->closeKey(); + OSL_ENSURE( + xRootKey->getKeyType( OUString( "LinkTest" ) ) == + registry::RegistryKeyType_LINK, + "test_SimpleRegistry error 1214" ); + + xKey = xRootKey->openKey("FirstKey/SecondSubKey"); + OSL_ENSURE( !xKey.is(), "test_SimpleRegistry error 27" ); + + // Test Links + xKey = xRootKey->createKey(OUString( "FifthKey" )); + xKey->createLink(OUString( "MyFirstLink" ), + OUString( "/ThirdKey/FirstSubKey" )); + + xKey = xRootKey->openKey("/FifthKey/MyFirstLink"); + OSL_ENSURE( xKey->isValid(), "test_SimpleRegistry error 27" ); + OSL_ENSURE( xKey->getKeyName() == "/ThirdKey/FirstSubKey", "test_SimpleRegistry error 28" ); + + xKey->createLink(OUString( "/WithSubSubKey/MyFourthLink" ), + OUString( "/FourthKey/MySecondLink" )); + + OSL_ENSURE( xKey->getLinkTarget(OUString( "/WithSubSubKey/MyFourthLink" )) + == "/FourthKey/MySecondLink", "test_SimpleRegistry error 29" ); + + try + { + OSL_ENSURE( xKey->getResolvedName(OUString( "/WithSubSubKey/MyFourthLink/BlaBlaBla" )) + == "/FourthKey/MySecondLink/BlaBlaBla", "test_SimpleRegistry error 30" ); + } + catch(InvalidRegistryException&) + { + } + + xRootKey->createLink(OUString( "/FourthKey/MySecondLink" ), + OUString( "/SixthKey/MyThirdLink" )); + xKey = xRootKey->createKey(OUString( "SixthKey" )); + xKey->createLink(OUString( "MyThirdLink" ), + OUString( "/FourthKey/MySecondLink" )); + + xKey = xRootKey->createKey(OUString( "/SixthKey/SixthSubKey" )); + + try + { + xRootKey->openKey("/FifthKey/MyFirstLink/WithSubSubKey/MyFourthLink"); + } + catch(InvalidRegistryException&) + { + } + + OSL_ENSURE( xRootKey->getLinkTarget(OUString( "/FifthKey/MyFirstLink/WithSubSubKey/MyFourthLink" )) + == "/FourthKey/MySecondLink", "test_SimpleRegistry error 31" ); + + xRootKey->deleteLink(OUString( "/FifthKey/MyFirstLink/WithSubSubKey/MyFourthLink" )); + + xRootKey->createLink(OUString( "/FourthKey/MySecondLink" ), + OUString( "/ThirdKey/FirstSubKey/WithSubSubKey" )); + + xKey = xRootKey->openKey("SixthKey"); + seqNames = xKey->getKeyNames(); + seqKeys = xKey->openKeys(); + + OSL_ENSURE( seqNames.getArray()[0] == "/SixthKey/SixthSubKey", + "test_SimpleRegistry error 32" ); + OSL_ENSURE( seqNames.getArray()[1] == "/SixthKey/MyThirdLink", + "test_SimpleRegistry error 33" ); + + OSL_ENSURE( seqKeys.getArray()[0]->getKeyName() == "/SixthKey/SixthSubKey", + "test_SimpleRegistry error 34" ); + OSL_ENSURE( seqKeys.getArray()[1]->getKeyName() == "/ThirdKey/FirstSubKey/WithSubSubKey", + "test_SimpleRegistry error 35" ); + + xRootKey->deleteLink(OUString( "/FourthKey/MySecondLink" )); + xRootKey->closeKey(); + } + catch(InvalidRegistryException&) + { + OSL_ENSURE(0, "exception InvalidRegistryExcption raised while doing test_SimpleRegistry"); + } + catch(InvalidValueException&) + { + OSL_ENSURE(0, "exception InvalidValueExcption raised while doing test_SimpleRegistry()"); + } + + xReg.clear(); + + printf("Test SimpleRegistry, OK!\n"); +} + + +void test_DefaultRegistry( + OUString const & testreg, + OUString const & testreg2, + bool bMergeDifferently = false ) +{ + // Test NestedRegistry + OUString exePath( getExePath() ); + OUString userRdb(exePath); + OUString applicatRdb(exePath); + + userRdb += "user.rdb"; + applicatRdb += "stoctest.rdb"; + + Reference < XMultiServiceFactory > rSMgr = ::cppu::createRegistryServiceFactory( userRdb, applicatRdb, sal_False, OUString()); + //OUString("//./e:/src596/stoc/wntmsci3/bin") ); + + Reference< XPropertySet > xPropSet( rSMgr, UNO_QUERY); + OSL_ENSURE( xPropSet.is(), "test_DefaultRegistry error0"); + + Any aPropertyAny( xPropSet->getPropertyValue("Registry") ); + OSL_ENSURE( aPropertyAny.hasValue(), "test_DefaultRegistry error1"); + + Reference<XSimpleRegistry> xReg; + aPropertyAny >>= xReg; + OSL_ENSURE( xReg.is(), "test_DefaultRegistry error1a"); + + Reference<XServiceInfo> xServInfo( Reference<XServiceInfo>::query(xReg) ); + + OSL_ENSURE( xServInfo.is(), "test_DefaultRegistry error2"); + + OSL_ENSURE( xServInfo->getImplementationName() == "com.sun.star.comp.stoc.NestedRegistry", "test_DefaultRegistry error3"); + OSL_ENSURE( xServInfo->supportsService("com.sun.star.registry.NestedRegistry"), "test_DefaultRegistry error4"); + OSL_ENSURE( xServInfo->getSupportedServiceNames().getLength() == 1, "test_DefaultRegistry error5"); + xServInfo.clear(); + + OSL_ENSURE( xReg.is(), "test_DefaultRegistry error6"); + + try + { + Reference<XRegistryKey> xRootKey(xReg->getRootKey()); + + Reference<XRegistryKey> xKey = xRootKey->openKey("/UCR/com/sun/star/registry/XSimpleRegistry"); + + OSL_ENSURE( xKey->getKeyName() == "/UCR/com/sun/star/registry/XSimpleRegistry", + "test_DefaultRegistry error 7" ); + + if (bMergeDifferently) + { + mergeKeys( + xReg, + OUString( "Test" ), + testreg ); + } + else + { + xReg->mergeKey(OUString( "Test" ), testreg ); + } + + xKey = xRootKey->openKey("Test/ThirdKey/FirstSubKey/WithSubSubKey"); + if (xKey.is()) + xKey->setLongValue(123456789); + + xKey = xRootKey->openKey("Test/ThirdKey/FirstSubKey"); + if (xKey.is()) + { + xKey->createKey(OUString( "SecondSubSubKey" )); + + Sequence<OUString> seqNames = xKey->getKeyNames(); + + OSL_ENSURE( seqNames.getLength() == 2, "test_DefaultRegistry error 8" ); + } + + xKey = xRootKey->openKey("/Test/ThirdKey"); + if (xKey.is()) + { + RegistryValueType valueType = xKey->getValueType(); + OSL_ENSURE( valueType == RegistryValueType_ASCIILIST, "test_DefaultRegistry error 9" ); + + Sequence<OUString> seqValue = xKey->getAsciiListValue(); + + OSL_ENSURE( seqValue.getLength() == 3, "test_DefaultRegistry error 10" ); + OSL_ENSURE( seqValue.getArray()[0] == "Hello", + "test_DefaultRegistry error 11" ); + OSL_ENSURE( seqValue.getArray()[1] == "here I", + "test_DefaultRegistry error 12" ); + OSL_ENSURE( seqValue.getArray()[2] == "come", + "test_DefaultRegistry error 13" ); + + xKey->setLongListValue({ 1234, 4567, 7890 }); + + Sequence<sal_Int32> seqLongValue = xKey->getLongListValue(); + + OSL_ENSURE( seqLongValue.getLength() == 3, "test_DefaultRegistry error 14" ); + OSL_ENSURE( seqLongValue.getArray()[0] == 1234, "test_DefaultRegistry error 15" ); + OSL_ENSURE( seqLongValue.getArray()[1] == 4567, "test_DefaultRegistry error 16" ); + OSL_ENSURE( seqLongValue.getArray()[2] == 7890, "test_DefaultRegistry error 17" ); + } + + // Test Links + xKey = xRootKey->createKey(OUString( "/Test/FifthKey" )); + xKey->createLink(OUString( "MyFirstLink" ), + OUString( "/Test/ThirdKey/FirstSubKey" )); + + xKey = xRootKey->openKey("/Test/FifthKey/MyFirstLink"); + OSL_ENSURE( xKey->isValid(), "test_DefaultRegistry error 18" ); + OSL_ENSURE( xKey->getKeyName() == "/Test/ThirdKey/FirstSubKey", + "test_DefaultRegistry error 19" ); + + xKey->createLink(OUString( "/WithSubSubKey/MyFourthLink" ), + OUString( "/Test/FourthKey/MySecondLink" )); + + OSL_ENSURE( xKey->getLinkTarget(OUString( "/WithSubSubKey/MyFourthLink" )) + == "/Test/FourthKey/MySecondLink", + "test_DefaultRegistry error 20" ); + + try + { + OSL_ENSURE( xKey->getResolvedName(OUString( "/WithSubSubKey/MyFourthLink/BlaBlaBla" )) + == "/Test/FourthKey/MySecondLink/BlaBlaBla", + "test_DefaultRegistry error 21" ); + } + catch(InvalidRegistryException&) + { + } + + xRootKey->createLink(OUString( "/Test/FourthKey/MySecondLink" ), + OUString( "/Test/SixthKey/MyThirdLink" )); + xKey = xRootKey->createKey(OUString( "/Test/SixthKey" )); + xKey->createLink(OUString( "MyThirdLink" ), + OUString( "/Test/FourthKey/MySecondLink" )); + + try + { + xRootKey->openKey("/Test/FifthKey/MyFirstLink/WithSubSubKey/MyFourthLink"); + } + catch(InvalidRegistryException&) + { + printf("test InvalidRegistryExcption OK!\n"); + } + + OSL_ENSURE( xRootKey->getLinkTarget(OUString( "/Test/FifthKey/MyFirstLink/WithSubSubKey/MyFourthLink" )) + == "/Test/FourthKey/MySecondLink", + "test_DefaultRegistry error 22" ); + + xRootKey->deleteLink(OUString( "/Test/FifthKey/MyFirstLink/WithSubSubKey/MyFourthLink" )); + + xKey = xRootKey->openKey("/Test/DefaultLink/SecondSubSubKey"); + if (xKey.is()) + { + OSL_ENSURE( xKey->getKeyName() == "/Test/ThirdKey/FirstSubKey/SecondSubSubKey", "test_DefaultRegistry error 23" ); + } + xKey = xRootKey->createKey(OUString( "/Test/DefaultLink/ThirdSubSubKey" )); + if (xKey.is()) + { + OSL_ENSURE( xKey->getKeyName() == "/Test/ThirdKey/FirstSubKey/ThirdSubSubKey", + "test_DefaultRegistry error 24" ); + } + + xKey = xRootKey->openKey("Test"); + OSL_ENSURE( xKey->isValid(), "test_DefaultRegistry error 25" ); + + xRootKey->deleteKey(OUString( "Test" )); + + if (bMergeDifferently) + { + mergeKeys( + xReg, + OUString( "AllFromTestreg2" ), + testreg2); + } + else + { + xReg->mergeKey(OUString( "AllFromTestreg2" ), + testreg2); + } + + xKey = xRootKey->openKey("/AllFromTestreg2/ThirdKey/FirstSubKey"); + if (xKey.is()) + { + xRootKey->deleteKey(OUString( "/AllFromTestreg2" )); + } + + } + catch(InvalidRegistryException&) + { + OSL_ENSURE(0, "exception InvalidRegistryExcption raised while doing test_DefaultRegistry"); + } + catch(InvalidValueException&) + { + OSL_ENSURE(0, "exception InvalidValueExcption raised while doing test_DefaultRegistry()"); + } + try + { + xReg->close(); + } + catch(const InvalidRegistryException& e) + { + (void)e; + OSL_ENSURE(0, OUStringToOString(e.Message,RTL_TEXTENCODING_ASCII_US).getStr()); + } + + + xReg.clear(); + + // shutdown + Reference< css::lang::XComponent > xComp( rSMgr, UNO_QUERY ); + OSL_ENSURE( xComp.is(), "### service manager has to implement XComponent!" ); + xComp->dispose(); + + printf("Test DefaultRegistry, OK!\n"); +} + + +SAL_IMPLEMENT_MAIN() +{ +// setStarUserRegistry(); + setLinkInDefaultRegistry(OUString("/Test/DefaultLink"), + OUString("/Test/FifthKey/MyFirstLink")); + + OUString reg1( "testreg1.rdb" ); + OUString reg2( "testreg2.rdb" ); + OUString areg1( "atestreg1.rdb" ); + OUString areg2( "atestreg2.rdb" ); + + test_SimpleRegistry( reg1, reg2 ); + test_DefaultRegistry( reg1, reg2 ); + test_SimpleRegistry( areg1, areg2, true ); // use different merge + test_DefaultRegistry( areg1, areg2, true ); + + Reference< XSimpleRegistry > xSimReg( ::cppu::createSimpleRegistry() ); + xSimReg->open( reg1, sal_False, sal_True ); + xSimReg->destroy(); + xSimReg->open( reg2, sal_False, sal_True ); + xSimReg->destroy(); + xSimReg->open( areg1, sal_False, sal_True ); + xSimReg->destroy(); + xSimReg->open( areg2, sal_False, sal_True ); + xSimReg->destroy(); + return 0; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testsmgr.cxx b/stoc/test/testsmgr.cxx new file mode 100644 index 0000000000..a8243386e2 --- /dev/null +++ b/stoc/test/testsmgr.cxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <stdio.h> + +#include <sal/main.h> +#include <osl/process.h> +#include <registry/registry.hxx> +#include <uno/mapping.hxx> + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> + +extern "C" void SAL_CALL test_ServiceManager(); + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +OString userRegEnv("STAR_USER_REGISTRY="); + +OUString getExePath() +{ + OUString exe; + + OSL_VERIFY(osl_getExecutableFile(&exe.pData) == osl_Process_E_None); + +#if defined(_WIN32) + exe = exe.copy(0, exe.getLength() - 16); +#else + exe = exe.copy(0, exe.getLength() - 12); +#endif + return exe; +} + +void setStarUserRegistry() +{ + Registry* myRegistry = new Registry(); + + RegistryKey rootKey, rKey, rKey2; + + OUString userReg = getExePath(); + userReg += "user.rdb"; + if (myRegistry->open(userReg, RegAccessMode::READWRITE)) + { + OSL_VERIFY(!myRegistry->create(userReg)); + } + + OSL_VERIFY(!myRegistry->close()); + delete myRegistry; + + userRegEnv += OUStringToOString(userReg, RTL_TEXTENCODING_ASCII_US); + putenv((char*)userRegEnv.getStr()); +} + +SAL_IMPLEMENT_MAIN() +{ + printf("ServiceManagerTest : \r"); + setStarUserRegistry(); + fflush(stdout); + test_ServiceManager(); + + printf("ServiceManagerTest : OK\n"); + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testsmgr2.cxx b/stoc/test/testsmgr2.cxx new file mode 100644 index 0000000000..2e40c0444d --- /dev/null +++ b/stoc/test/testsmgr2.cxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <stdio.h> + +#include <sal/main.h> +#include <cppuhelper/bootstrap.hxx> + +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +using namespace ::cppu; +using namespace css::uno; +using namespace css::lang; +using namespace css::container; +using namespace css::registry; + +SAL_IMPLEMENT_MAIN() +{ + try + { + + Reference< XSimpleRegistry > r1 = createSimpleRegistry(); + Reference< XSimpleRegistry > r2 = createSimpleRegistry(); + r1->open( OUString( "test1.rdb" ), sal_True, sal_False ); + r2->open( OUString( "test2.rdb" ), sal_True, sal_False ); + Reference< XSimpleRegistry > r = createNestedRegistry( ); + Reference< XInitialization > rInit( r, UNO_QUERY ); + Sequence< Any > seq( 2 ); + seq[0] <<= r1; + seq[1] <<= r2; + rInit->initialize( seq ); + + Reference< XComponentContext > rComp = bootstrap_InitialComponentContext( r ); + + Reference< XContentEnumerationAccess > xCtAccess( rComp->getServiceManager(), UNO_QUERY ); + + Reference< XEnumeration > rEnum = + xCtAccess->createContentEnumeration( OUString( "com.sun.star.bridge.Bridge" ) ); + + sal_Int32 n = 0; + while( rEnum->hasMoreElements() ) + { + Reference< XServiceInfo > r3; + rEnum->nextElement() >>= r3; + OString o = OUStringToOString( r3->getImplementationName() , RTL_TEXTENCODING_ASCII_US ); + printf( "%s\n" , o.getStr() ); + Sequence< OUString > seq2 = r3->getSupportedServiceNames(); + for( int i = 0 ;i < seq2.getLength() ; i ++ ) + { + o = OUStringToOString( seq2[i] , RTL_TEXTENCODING_ASCII_US ); + printf( " %s\n" , o.getStr() ); + } + n ++; + } + // there are two services in two registries ! + OSL_ASSERT( 2 == n ); + if( 2 == n ) + { + printf( "test passed\n" ); + } + + Reference< XComponent > xComp( rComp, UNO_QUERY ); + xComp->dispose(); + try + { + xCtAccess->createContentEnumeration( + OUString( "blabla" ) ); + } + catch (DisposedException &) + { + printf( "already disposed results in DisposedException: ok.\n" ); + return 0; + } + fprintf( stderr, "missing DisposedException!\n" ); + return 1; + } + catch ( const Exception & e ) + { + OString o = OUStringToOString( e.Message , RTL_TEXTENCODING_ASCII_US ); + printf( "%s\n" , o.getStr() ); + OSL_ASSERT( 0 ); + return 1; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testsmgr_cpnt.cxx b/stoc/test/testsmgr_cpnt.cxx new file mode 100644 index 0000000000..0adc74f874 --- /dev/null +++ b/stoc/test/testsmgr_cpnt.cxx @@ -0,0 +1,302 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rtl/alloc.h> +#include <osl/security.h> +#include <osl/thread.h> +#include <osl/mutex.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <uno/mapping.hxx> + + +#include <cppuhelper/factory.hxx> +#include <cppuhelper/servicefactory.hxx> +#include <cppuhelper/implbase.hxx> +#include <registry/registry.hxx> + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/registry/XImplementationRegistration.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> + +#include <stdio.h> + +#if defined ( UNX ) +#include <limits.h> +#define _MAX_PATH PATH_MAX +#endif + + +#define IMPLEMENTATION_NAME "com.sun.star.DummyService.V10" +#define SERVICE_NAME "com.sun.star.ts.TestManagerImpl" + + +using namespace css::uno; +using namespace css::registry; +using namespace css::lang; +using namespace css::container; +using namespace osl; +using namespace cppu; + + +Reference<XMultiServiceFactory> getProcessServiceManager() +{ + static Reference<XMultiServiceFactory> s_x( + createRegistryServiceFactory(OUString("stoctest.rdb"), sal_False)); + return s_x; +} + +Reference< XMultiServiceFactory > createRegistryServiceManager( const OUString& registryName ) +{ + return createRegistryServiceFactory( registryName ); +} + + +/********************************** +* The service, that is used to test the Service manager +* +* +* +*************************************/ +static sal_uInt32 nInstanceCount = 0; +class Test_Manager_Impl : public WeakImplHelper< XServiceInfo > +{ +public: + Test_Manager_Impl(){ nInstanceCount++;} + ~Test_Manager_Impl(); + + // XServiceInfo + OUString SAL_CALL getImplementationName() throw(); + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) throw(); + Sequence< OUString > SAL_CALL getSupportedServiceNames() throw(); + static Sequence< OUString > SAL_CALL getSupportedServiceNames_Static() throw(); + +private: +// static XIdlClassRef getStaticIdlClass(); +}; + +Test_Manager_Impl::~Test_Manager_Impl() +{ + nInstanceCount--; +} + + +// old, is no longer needed by the new Mimic +Reference< XInterface > SAL_CALL Test_Manager_Impl_CreateInstance_Impl() +{ + return (OWeakObject *)new Test_Manager_Impl(); +} + + +// Test_Manager_Impl_CreateInstance() + +Reference < XInterface > SAL_CALL Test_Manager_Impl_CreateInstance( + const Reference< XMultiServiceFactory > & /*rSMgr*/ ) throw (Exception) +{ + Reference < XInterface > xService = (XWeak *)(OWeakObject *)new Test_Manager_Impl( ); + + return xService; +} + + +// Test_Manager_Impl::getImplementationName + +OUString Test_Manager_Impl::getImplementationName() throw() +{ + return OUString(IMPLEMENTATION_NAME); +} + +// Test_Manager_Impl::supportsService +sal_Bool Test_Manager_Impl::supportsService( const OUString& ServiceName ) throw() +{ + return cppu::supportsService(this, ServiceName); +} + + +// Test_Manager_Impl::getSupportedServiceNames + +Sequence< OUString > Test_Manager_Impl::getSupportedServiceNames() throw () +{ + return getSupportedServiceNames_Static(); +} + + +// Test_Manager_Impl::getSupportedServiceNames_Static + +Sequence< OUString > Test_Manager_Impl::getSupportedServiceNames_Static() throw () +{ + return { SERVICE_NAME, "com.sun.star.bridge.Bridge" }; +} + + +/**** +* +* +* This routine performs the test of the process service manager ( getProcessServiceManager is called ) +* +* +* +****/ + +extern "C" void SAL_CALL test_ServiceManager() +{ +#if ! defined SAL_DLLPREFIX +#define SAL_DLLPREFIX "" +#endif + OUString atUModule2 = SAL_DLLPREFIX "testsmgr_component" SAL_DLLEXTENSION ; + + // expand shared library name + OString atModule2( OUStringToOString(atUModule2, RTL_TEXTENCODING_ASCII_US) ); + + // get the process servicemanager + Reference <XMultiServiceFactory> xSMgr = getProcessServiceManager(); + + OSL_ENSURE( xSMgr.is() , "query on XServiceManager failed" ); + + Reference<XContentEnumerationAccess> xContEnum(xSMgr, UNO_QUERY); + OSL_ENSURE( xContEnum.is() , "query on XContentEnumerationAccess failed" ); + Reference<XEnumeration > xEnum(xContEnum->createContentEnumeration(OUString("com.sun.star.registry.SimpleRegistry"))); + OSL_ENSURE( xEnum.is() , "createContentEnumeration failed" ); + sal_Int32 nLen = 0; + while( xEnum->hasMoreElements() ) + { + nLen++; + xEnum->nextElement(); + } + OSL_ENSURE( nLen == 1, "more than one implementation for SimpleRegistry" ); + + Reference<XEnumerationAccess> xImplEnum(xSMgr, UNO_QUERY); + OSL_ENSURE( xImplEnum.is() , "query on XEnumeration failed" ); + xEnum.set(xImplEnum->createEnumeration()); + OSL_ENSURE( xEnum.is() , "createEnumeration failed" ); + nLen = 0; + while( xEnum->hasMoreElements() ) + { + nLen++; + Reference< XServiceInfo > sf( xEnum->nextElement(), UNO_QUERY ); + OString str( OUStringToOString( sf->getImplementationName(), RTL_TEXTENCODING_ASCII_US ) ); + ::fprintf( stderr, "> implementation name: %s\n", str.getStr() ); + } + OSL_ENSURE( nLen == 8, "more than 6 factories" ); + + // try to get an instance for an unknown service + OSL_VERIFY( !xSMgr->createInstance("bla.blup.Q").is() ); + + + // First test : register service via the internal function of the component itself + + { + Reference< XImplementationRegistration > + xInst( xSMgr->createInstance("com.sun.star.registry.ImplementationRegistration"), UNO_QUERY ); + OSL_ENSURE( xInst.is(), "no ImplementationRegistration" ); + + try { + // register the services via writeComponentRegInfo (see at end of this file) + xInst->registerImplementation(OUString("com.sun.star.loader.SharedLibrary"), atUModule2, Reference< XSimpleRegistry >() ); + } + catch(const CannotRegisterImplementationException &) { + OSL_ENSURE( 0, "register implementation failed" ); + } + + // getImplementations() check + Sequence<OUString> seqImpl = xInst->getImplementations(OUString("com.sun.star.loader.SharedLibrary"), atUModule2); + OSL_ENSURE( seqImpl.getLength() == 1, "count of implementations is wrong" ); + OSL_ENSURE( seqImpl.getConstArray()[0] == "com.sun.star.DummyService.V10", "implementation name is not equal" ); + + + // tests, if a service provider can be instantiated. + + Reference< XInterface > xIFace(xSMgr->createInstance("com.sun.star.ts.TestManagerImpl")); + OSL_ENSURE( xIFace.is(), "loadable service not found" ); + + // remove the service + OSL_VERIFY( xInst->revokeImplementation(atUModule2, Reference< XSimpleRegistry > ()) ); + } + + Reference<XComponent> xComp(xSMgr, UNO_QUERY); + xComp->dispose(); + + xComp.clear(); + xSMgr.clear(); +} + + +extern "C" +{ + +sal_Bool SAL_CALL component_writeInfo( + void * /*pServiceManager*/, void * pRegistryKey ) +{ + if (pRegistryKey) + { + try + { + Reference< XRegistryKey > xNewKey( + reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey( + OUString( "/" IMPLEMENTATION_NAME "/UNO/SERVICES" ) ) ); + + const Sequence< OUString > & rSNL = + Test_Manager_Impl::getSupportedServiceNames_Static(); + const OUString * pArray = rSNL.getConstArray(); + for ( sal_Int32 nPos = rSNL.getLength(); nPos--; ) + xNewKey->createKey( pArray[nPos] ); + + return sal_True; + } + catch (InvalidRegistryException &) + { + OSL_FAIL( "### InvalidRegistryException!" ); + } + } + return sal_False; +} + +SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory( + const char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ ) +{ + void * pRet = 0; + + if (rtl_str_compare( pImplName, IMPLEMENTATION_NAME ) == 0) + { + Reference< XSingleServiceFactory > xFactory( createSingleFactory( + reinterpret_cast< XMultiServiceFactory * >( pServiceManager ), + OUString( IMPLEMENTATION_NAME ), + Test_Manager_Impl_CreateInstance, + Test_Manager_Impl::getSupportedServiceNames_Static() ) ); + + if (xFactory.is()) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + } + + return pRet; +} +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/test/testsmgr_cpnt.map b/stoc/test/testsmgr_cpnt.map new file mode 100644 index 0000000000..0bd933d7e8 --- /dev/null +++ b/stoc/test/testsmgr_cpnt.map @@ -0,0 +1,25 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +UDK_3_0_0 { + global: + component_writeInfo; + component_getFactory; + test_ServiceManager; + local: + *; +}; diff --git a/stoc/test/uriproc/test_uriproc.cxx b/stoc/test/uriproc/test_uriproc.cxx new file mode 100644 index 0000000000..aa96e6a86b --- /dev/null +++ b/stoc/test/uriproc/test_uriproc.cxx @@ -0,0 +1,1420 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp> +#include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrlReference.hpp> +#include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> +#include <com/sun/star/util/XMacroExpander.hpp> +#include <cppuhelper/bootstrap.hxx> +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <rtl/string.hxx> +#include <rtl/textenc.h> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <sal/macros.h> + +namespace { + +#define TEST_ASSERT_EQUAL(token1, token2, token3, expected, actual) \ + CPPUNIT_ASSERT_EQUAL_MESSAGE( \ + createTestAssertEqualMessage( \ + token1, token2, token3, #expected, #actual, expected, actual). \ + getStr(), \ + (expected), (actual)) + +template< typename T > void append( + OUStringBuffer & buffer, T const & value) +{ + buffer.append(value); +} + +template<> void append(OUStringBuffer & buffer, bool const & value) { + buffer.append(value); +} + +template<> void append(OUStringBuffer & buffer, std::size_t const & value) +{ + buffer.append(static_cast< sal_Int32 >(value)); +} + +template<> void append(OUStringBuffer & buffer, char const * const & value) +{ + buffer.appendAscii(value); +} + +template< typename T1, typename T2, typename T3, typename T4 > +OString createTestAssertEqualMessage( + char const * token1, T1 const & token2, T2 const & token3, + char const * expectedExpr, char const * actualExpr, T3 const & expected, + T4 const & actual) +{ + OUStringBuffer buf(512); + buf.appendAscii(token1); + buf.append('|'); + append(buf, token2); + buf.append('|'); + append(buf, token3); + buf.append(": TEST_ASSERT_EQUAL("); + buf.appendAscii(expectedExpr); + buf.append(", "); + buf.appendAscii(actualExpr); + buf.append("): <"); + append(buf, expected); + buf.append("> != <"); + append(buf, actual); + buf.append('>'); + return OUStringToOString( + buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US); +} + +class Test: public CppUnit::TestFixture { +public: + virtual void setUp(); + + virtual void tearDown(); + + void testParse(); + + void testMakeAbsolute(); + + void testMakeRelative(); + + void testVndSunStarExpand(); + + void testVndSunStarScript(); + + void testTranslator(); + + void testPkgUrlFactory(); + + CPPUNIT_TEST_SUITE(Test); + CPPUNIT_TEST(testParse); + CPPUNIT_TEST(testMakeAbsolute); + CPPUNIT_TEST(testMakeRelative); + CPPUNIT_TEST(testVndSunStarExpand); + CPPUNIT_TEST(testVndSunStarScript); + CPPUNIT_TEST(testTranslator); + CPPUNIT_TEST(testPkgUrlFactory); + CPPUNIT_TEST_SUITE_END(); + +private: + css::uno::Reference< css::uno::XComponentContext > m_context; + css::uno::Reference< css::uri::XUriReferenceFactory > m_uriFactory; +}; + +void Test::setUp() { + m_context = cppu::defaultBootstrap_InitialComponentContext(); + m_uriFactory = css::uri::UriReferenceFactory::create(m_context); +} + +void Test::tearDown() { + m_uriFactory.clear(); + css::uno::Reference< css::lang::XComponent >( + m_context, css::uno::UNO_QUERY_THROW)->dispose(); +} + +void Test::testParse() { + struct Data { + char const * uriReference; + char const * scheme; + char const * schemeSpecificPart; + bool isHierarchical; + char const * authority; + char const * path; + bool hasRelativePath; + sal_Int32 pathSegmentCount; + char const * pathSegment0; + char const * pathSegment1; + char const * pathSegment2; + char const * pathSegment3; + char const * pathSegment4; + char const * query; + char const * fragment; + }; + Data data[] = { + { "", nullptr, "", true, nullptr, + "", true, 0, "", "", "", "", "", nullptr, nullptr }, + { "scheme:", "scheme", "", false, nullptr, + "", true, 0, "", "", "", "", "", nullptr, nullptr }, + { "scheme:/", "scheme", "/", true, nullptr, + "/", false, 1, "", "", "", "", "", nullptr, nullptr }, + { "scheme://", "scheme", "//", true, "", + "", false, 0, "", "", "", "", "", nullptr, nullptr }, + { "scheme:///", "scheme", "///", true, "", + "/", false, 1, "", "", "", "", "", nullptr, nullptr }, + { "scheme:////", "scheme", "////", true, "", + "//", false, 2, "", "", "", "", "", nullptr, nullptr }, + { "scheme:////", "scheme", "////", true, "", + "//", false, 2, "", "", "", "", "", nullptr, nullptr }, + { "scheme:#", "scheme", "", false, nullptr, + "", true, 0, "", "", "", "", "", nullptr, "" }, + { "scheme:?", "scheme", "?", false, nullptr, + "", true, 0, "", "", "", "", "", "", nullptr }, + { "/", nullptr, "/", true, nullptr, + "/", false, 1, "", "", "", "", "", nullptr, nullptr }, + { "//", nullptr, "//", true, "", + "", false, 0, "", "", "", "", "", nullptr, nullptr }, + { "///", nullptr, "///", true, "", + "/", false, 1, "", "", "", "", "", nullptr, nullptr }, + { "////", nullptr, "////", true, "", + "//", false, 2, "", "", "", "", "", nullptr, nullptr } }; + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + css::uno::Reference< css::uri::XUriReference > uriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].uriReference))); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, data[i].schemeSpecificPart != nullptr, + uriRef.is()); + if (uriRef.is()) { + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].uriReference), + uriRef->getUriReference()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].scheme != nullptr, bool(uriRef->isAbsolute())); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii( + data[i].scheme == nullptr ? "" : data[i].scheme), + uriRef->getScheme()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].schemeSpecificPart), + uriRef->getSchemeSpecificPart()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].isHierarchical, + static_cast< bool >(uriRef->isHierarchical())); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].authority != nullptr, bool(uriRef->hasAuthority())); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii( + data[i].authority == nullptr ? "" : data[i].authority), + uriRef->getAuthority()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].path), + uriRef->getPath()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].hasRelativePath, + static_cast< bool >(uriRef->hasRelativePath())); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].pathSegmentCount, uriRef->getPathSegmentCount()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString(), uriRef->getPathSegment(-1)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].pathSegment0), + uriRef->getPathSegment(0)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].pathSegment1), + uriRef->getPathSegment(1)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].pathSegment2), + uriRef->getPathSegment(2)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].pathSegment3), + uriRef->getPathSegment(3)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii(data[i].pathSegment4), + uriRef->getPathSegment(4)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString(), uriRef->getPathSegment(5)); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].query != nullptr, bool(uriRef->hasQuery())); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii( + data[i].query == nullptr ? "" : data[i].query), + uriRef->getQuery()); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + data[i].fragment != nullptr, bool(uriRef->hasFragment())); + TEST_ASSERT_EQUAL( + "testParse", i, data[i].uriReference, + OUString::createFromAscii( + data[i].fragment == nullptr ? "" : data[i].fragment), + uriRef->getFragment()); + } + } +} + +void Test::testMakeAbsolute() { + struct Data { + char const * baseUriReference; + char const * uriReference; + bool processSpecialBaseSegments; + css::uri::RelativeUriExcessParentSegments excessParentSegments; + char const * absolute; + }; + Data data[] = { + // The following tests are taken from RFC 3986, Section 5.4: + { "http://a/b/c/d;p?q", "g:h", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "g:h" }, + { "http://a/b/c/d;p?q", "g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/g" }, + { "http://a/b/c/d;p?q", "./g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/g" }, + { "http://a/b/c/d;p?q", "g/", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/g/" }, + { "http://a/b/c/d;p?q", "/g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/g" }, + { "http://a/b/c/d;p?q", "//g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://g" }, + { "http://a/b/c/d;p?q", "?y", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/d;p?y" }, + { "http://a/b/c/d;p?q", "g?y", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g?y" }, + { "http://a/b/c/d;p?q", "#s", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/d;p?q#s" }, + { "http://a/b/c/d;p?q", "g#s", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g#s" }, + { "http://a/b/c/d;p?q", "g?y#s", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g?y#s" }, + { "http://a/b/c/d;p?q", ";x", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/;x" }, + { "http://a/b/c/d;p?q", "g;x", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g;x" }, + { "http://a/b/c/d;p?q", "g;x?y#s", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g;x?y#s" }, + { "http://a/b/c/d;p?q", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/d;p?q" }, + { "http://a/b/c/d;p?q", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/" }, + { "http://a/b/c/d;p?q", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/" }, + { "http://a/b/c/d;p?q", "..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/" }, + { "http://a/b/c/d;p?q", "../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/" }, + { "http://a/b/c/d;p?q", "../g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/g" }, + { "http://a/b/c/d;p?q", "../..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/" }, + { "http://a/b/c/d;p?q", "../../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/" }, + { "http://a/b/c/d;p?q", "../../g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/g" }, + { "http://a/b/c/d;p?q", "../../../g", true, + css::uri::RelativeUriExcessParentSegments_ERROR, nullptr }, + { "http://a/b/c/d;p?q", "../../../g", true, + css::uri::RelativeUriExcessParentSegments_RETAIN, "http://a/../g" }, + { "http://a/b/c/d;p?q", "../../../g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/g" }, + { "http://a/b/c/d;p?q", "../../../../g", true, + css::uri::RelativeUriExcessParentSegments_ERROR, nullptr }, + { "http://a/b/c/d;p?q", "../../../../g", true, + css::uri::RelativeUriExcessParentSegments_RETAIN, + "http://a/../../g" }, + { "http://a/b/c/d;p?q", "../../../../g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/g" }, + { "http://a/b/c/d;p?q", "/./g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/g" }, + { "http://a/b/c/d;p?q", "/../g", true, + css::uri::RelativeUriExcessParentSegments_ERROR, nullptr }, + { "http://a/b/c/d;p?q", "/../g", true, + css::uri::RelativeUriExcessParentSegments_RETAIN, "http://a/../g" }, + { "http://a/b/c/d;p?q", "/../g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/g" }, + { "http://a/b/c/d;p?q", "g.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/g." }, + { "http://a/b/c/d;p?q", ".g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/.g" }, + { "http://a/b/c/d;p?q", "g..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g.." }, + { "http://a/b/c/d;p?q", "..g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/..g" }, + { "http://a/b/c/d;p?q", "./../g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/g" }, + { "http://a/b/c/d;p?q", "./g/.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/g/" }, + { "http://a/b/c/d;p?q", "g/./h", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g/h" }, + { "http://a/b/c/d;p?q", "g/../h", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/h" }, + { "http://a/b/c/d;p?q", "g;x=1/./y", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g;x=1/y" }, + { "http://a/b/c/d;p?q", "g;x=1/../y", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http://a/b/c/y" }, + { "http://a/b/c/d;p?q", "g?y/./x", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g?y/./x" }, + { "http://a/b/c/d;p?q", "g?y/../x", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g?y/../x" }, + { "http://a/b/c/d;p?q", "g#s/./x", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g#s/./x" }, + { "http://a/b/c/d;p?q", "g#s/../x", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, + "http://a/b/c/g#s/../x" }, + { "http://a/b/c/d;p?q", "http:g", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "http:g" }, + + { "scheme:", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + + { "scheme://a", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a" }, + { "scheme://a", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + + { "scheme://a/", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + + { "scheme://a/b", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b" }, + { "scheme://a/b", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + { "scheme://a/b", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/" }, + + { "scheme://a/b/", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + { "scheme://a/b/", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a/b/" }, + + { "scheme:a", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a" }, + { "scheme:a", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:a", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:a", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:a", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:a", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:" }, + { "scheme:a", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:a", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + + { "scheme:a/", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + + { "scheme:a/b", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b" }, + { "scheme:a/b", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + { "scheme:a/b", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/" }, + + { "scheme:a/b/", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + { "scheme:a/b/", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:a/b/" }, + + { "scheme:/a", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a" }, + { "scheme:/a", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + { "scheme:/a", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/" }, + + { "scheme:/a/", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + + { "scheme:/a/b", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b" }, + { "scheme:/a/b", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + { "scheme:/a/b", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/" }, + + { "scheme:/a/b/", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", ".", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/../", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./././x/..", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./x/../.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./x/.././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "././x/.././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "././x/../././", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + { "scheme:/a/b/", "./././x/../././.", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme:/a/b/" }, + + { "scheme://a#s", "", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a" }, + { "scheme://a", "?q", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a?q" }, + { "scheme://a#s", "?q", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a?q" }, + { "scheme://a", "#s", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a#s" }, + { "scheme://a#s1", "#s2", true, + css::uri::RelativeUriExcessParentSegments_REMOVE, "scheme://a#s2" }, + + { "schema://a", "schema://b/c/../d", true, css::uri::RelativeUriExcessParentSegments_REMOVE, + "schema://b/d" }, + + // Per RFC 3986 Section 5.2.1 "Pre-parse the Base URI", "Normalization of the base URI + // [...; esp. dot-segment removal per Section 6.2.2.3 "Path Segment Normalization"] is + // optional" (and not done by our implementation), so if the relative URI has no scheme and + // no authority and an empty path, the Base URI's path is used unmodified per Section 5.2.2 + // "Transform References" and thus still contains dot-segments: + { "scheme:/a/../b/c", "", true, css::uri::RelativeUriExcessParentSegments_REMOVE, + "scheme:/a/../b/c" }, + { "scheme:/a/../b/c", "d", true, css::uri::RelativeUriExcessParentSegments_REMOVE, + "scheme:/b/d" } }; + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + css::uno::Reference< css::uri::XUriReference > baseUriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].baseUriReference))); + TEST_ASSERT_EQUAL("testMakeAbsolute", i, data[i].baseUriReference, true, baseUriRef.is()); + css::uno::Reference< css::uri::XUriReference > uriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].uriReference))); + TEST_ASSERT_EQUAL("testMakeAbsolute", i, data[i].uriReference, true, uriRef.is()); + css::uno::Reference< css::uri::XUriReference > absolute( + m_uriFactory->makeAbsolute( + baseUriRef, uriRef, data[i].processSpecialBaseSegments, + data[i].excessParentSegments)); + TEST_ASSERT_EQUAL( + "testMakeAbsolute", i, data[i].uriReference, + data[i].absolute != nullptr, absolute.is()); + if (absolute.is()) { + TEST_ASSERT_EQUAL( + "testMakeAbsolute", i, data[i].uriReference, + OUString::createFromAscii(data[i].absolute), + absolute->getUriReference()); + } + // For those test cases that conform to RFC 3986, check that the behavior matches + // rtl::Uri::convertRelToAbs: + if (data[i].processSpecialBaseSegments + && data[i].excessParentSegments == css::uri::RelativeUriExcessParentSegments_REMOVE) + { + try { + auto const absolute2 = rtl::Uri::convertRelToAbs( + OUString::createFromAscii(data[i].baseUriReference), + OUString::createFromAscii(data[i].uriReference)); + TEST_ASSERT_EQUAL( + "testMakeAbsolute", i, data[i].uriReference, true, data[i].absolute != nullptr); + TEST_ASSERT_EQUAL( + "testMakeAbsolute", i, data[i].uriReference, + OUString::createFromAscii(data[i].absolute), absolute2); + } catch (rtl::MalformedUriException &) { + TEST_ASSERT_EQUAL( + "testMakeAbsolute", i, data[i].uriReference, true, data[i].absolute == nullptr); + } + } + } +} + +void Test::testMakeRelative() { + struct Data { + char const * baseUriReference; + char const * uriReference; + bool preferAuthorityOverRelativePath; + bool preferAbsoluteOverRelativePath; + bool encodeRetainedSpecialSegments; + char const * relative; + char const * absolute; + }; + Data data[] = { + { "scheme1://a/b/c", "scheme2://a/b/c?q#s", true, true, false, + "scheme2://a/b/c?q#s", nullptr }, + { "scheme://a/b/c", "scheme:a/b/c?q#s", true, true, false, + "scheme:a/b/c?q#s", nullptr }, + { "scheme://a/b/c", "", true, true, false, "", "scheme://a/b/c" }, + { "scheme://a/b/c", "//d/e/f", true, true, false, "//d/e/f", + "scheme://d/e/f" }, + { "scheme://a/b/c", "./e?q#s", true, true, false, "./e?q#s", + "scheme://a/b/e?q#s" }, + { "scheme://a/b", "scheme://a?q", true, true, false, "/?q", + "scheme://a/?q" }, + { "scheme://a/b", "scheme://a?q", true, false, false, "/?q", + "scheme://a/?q" }, + { "scheme://a", "scheme://a?q", true, true, false, "?q", nullptr }, + { "scheme://a/", "scheme://a?q", true, true, false, "?q", + "scheme://a/?q" }, + { "scheme://a", "scheme://a/?q", true, true, false, "/?q", + nullptr }, + { "scheme://a/", "scheme://a/?q", true, true, false, "?q", + nullptr }, + { "scheme://a?q", "scheme://a?q", true, true, false, "", nullptr }, + { "scheme://a/?q", "scheme://a?q", true, true, false, "", + "scheme://a/?q" }, + { "scheme://a?q", "scheme://a/?q", true, true, false, "/?q", + nullptr }, + { "scheme://a/?q", "scheme://a/?q", true, true, false, "", nullptr }, + { "scheme://a/b/c/d", "scheme://a//", true, true, false, "//a//", nullptr }, + { "scheme://a/b/c/d", "scheme://a//", false, true, false, "../..//", + nullptr }, + { "scheme://a/b/c/d", "scheme://a//", true, false, false, "../..//", + nullptr }, + { "scheme://a/b/c/d", "scheme://a//", false, false, false, "../..//", + nullptr }, + { "scheme://a/b/c/d", "scheme://a/e", true, true, false, "/e", nullptr }, + { "scheme://a/b/c/d", "scheme://a/e", true, false, false, "../../e", + nullptr }, + { "scheme://a/b/c/d/e", "scheme://a/b/f", true, true, false, "../../f", + nullptr }, + { "scheme://a/b/c/d/e", "scheme://a/b", true, true, false, "/b", nullptr }, + { "scheme://a/b/c/d/e", "scheme://a/b", true, false, false, + "../../../b", nullptr }, + { "scheme://a/b/c/d/e", "scheme://a/b/", true, true, false, "../..", + nullptr }, + { "scheme://a/b/c/d/e", "scheme://a/b/c", true, true, false, "../../c", + nullptr }, + { "scheme://a/b/c/d/e", "scheme://a/b/c/", true, true, false, "..", nullptr }, + { "scheme://a/b/", "scheme://a/b/c/d", true, true, false, "c/d", nullptr }, + { "scheme://a/b/", "scheme://a/b/c/d/", true, true, false, "c/d/", nullptr }, + { "scheme://a/b/c", "scheme://a/b//", true, true, false, ".//", nullptr }, + { "scheme://a/b/c", "scheme://a/b//d", true, true, false, ".//d", nullptr }, + { "scheme://a/b/c", "scheme://a/b//d//", true, true, false, ".//d//", + nullptr }, + { "scheme://a/b/c", "scheme://a/b/d+:", true, true, false, "./d+:", nullptr }, + { "scheme://a/b/c", "scheme://a/b/+d:", true, true, false, "+d:", nullptr }, + { "scheme://a/b/c", "scheme://a/b/d#e:f", true, true, false, "d#e:f", + nullptr }, + { "scheme://a/b/c/", "scheme://a/b/../d/.e/.", true, true, false, + "../../d/.e/.", + "scheme://a/d/.e/" }, + { "scheme://a/b/c/", "scheme://a/b/../d/.e/.", true, true, true, + "../%2E%2E/d/.e/%2E", "scheme://a/b/%2E%2E/d/.e/%2E" }, + { "scheme://auth/a/b", "scheme://auth//c/d", true, true, false, + "//auth//c/d", nullptr }, + { "scheme://auth/a/b", "scheme://auth//c/d", false, true, false, + "..//c/d", nullptr }, + { "scheme://auth/a/b", "scheme://auth/c/d", true, true, false, "/c/d", + nullptr }, + { "scheme://auth/a/b", "scheme://auth/c/d", true, false, false, + "../c/d", nullptr }, + { "scheme:a/b/c", "scheme://d/e/f", true, true, false, "//d/e/f", nullptr }, + { "scheme:/a/b/c", "scheme://d/e/f", true, true, false, "//d/e/f", nullptr }, + { "scheme:a/b/c", "scheme:/d/e/f", true, true, false, "/d/e/f", nullptr }, + { "scheme:/a/b/c", "scheme:/d/e/f", true, true, false, "/d/e/f", nullptr }, + { "scheme:a/b/c", "scheme:d/e/f", true, true, false, "scheme:d/e/f", nullptr }, + { "scheme:/a/b/c", "scheme:d/e/f", true, true, false, "scheme:d/e/f", nullptr } }; + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + css::uno::Reference< css::uri::XUriReference > baseUriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].baseUriReference))); + TEST_ASSERT_EQUAL("testMakeRelative", i, data[i].baseUriReference, true, baseUriRef.is()); + css::uno::Reference< css::uri::XUriReference > uriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].uriReference))); + TEST_ASSERT_EQUAL("testMakeRelative", i, data[i].uriReference, true, uriRef.is()); + css::uno::Reference< css::uri::XUriReference > relative( + m_uriFactory->makeRelative( + baseUriRef, uriRef, data[i].preferAuthorityOverRelativePath, + data[i].preferAbsoluteOverRelativePath, + data[i].encodeRetainedSpecialSegments)); + TEST_ASSERT_EQUAL( + "testMakeRelative", i, data[i].uriReference, + data[i].relative != nullptr, relative.is()); + if (relative.is()) { + TEST_ASSERT_EQUAL( + "testMakeRelative", i, data[i].uriReference, + OUString::createFromAscii(data[i].relative), + relative->getUriReference()); + css::uno::Reference< css::uri::XUriReference > absolute( + m_uriFactory->makeAbsolute( + baseUriRef, relative, true, + css::uri::RelativeUriExcessParentSegments_ERROR)); + TEST_ASSERT_EQUAL("testMakeRelative", i, data[i].uriReference, true, absolute.is()); + TEST_ASSERT_EQUAL( + "testMakeRelative", i, data[i].uriReference, + OUString::createFromAscii( + data[i].absolute == nullptr + ? data[i].uriReference : data[i].absolute), + absolute->getUriReference()); + } + } +} + +void Test::testVndSunStarExpand() { + struct Data { + char const * uriReference; + char const * expanded; + }; + Data data[] = { + { "vnd.sun.star.expand:", "" }, // liberally accepted + { "vnd.sun.star.expand:/", "/" }, // liberally accepted + { "vnd.sun.star.expand:%80", nullptr }, + { "vnd.sun.star.expand:%5C$%5C%24%5C%5C", "$$\\" } }; + css::uno::Reference< css::util::XMacroExpander > expander( + m_context->getValueByName( + "/singletons/com.sun.star.util.theMacroExpander"), + css::uno::UNO_QUERY_THROW); + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + css::uno::Reference< css::uri::XUriReference > uriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].uriReference))); + TEST_ASSERT_EQUAL( + "testVndSunStarExpand", i, data[i].uriReference, + data[i].expanded != nullptr, uriRef.is()); + if (uriRef.is()) { + css::uno::Reference< css::uri::XVndSunStarExpandUrlReference > + expandUrl(uriRef, css::uno::UNO_QUERY_THROW); + TEST_ASSERT_EQUAL( + "testVndSunStarExpand", i, data[i].uriReference, + OUString::createFromAscii(data[i].expanded), + expandUrl->expand(expander)); + } + } +} + +void Test::testVndSunStarScript() { + struct Parameter { + char const * key; + char const * value; + }; + std::size_t const parameterCount = 3; + struct Data { + char const * uriReference; + char const * name; + const bool normalized; + Parameter parameters[parameterCount]; + }; + Data data[] = { + { "vnd.sun.star.script:", nullptr, false, {} }, + { "vnd.sun.star.script:/", nullptr, false, {} }, + { "vnd.sun.star.script:/abc/def?ghi=jkl&mno=pqr", nullptr, false, {} }, + { "vnd.sun.star.script:abc%3fdef/ghi", "abc?def/ghi", false, {} }, + { "vnd.sun.star.script:name?a", nullptr, false, {} }, + { "vnd.sun.star.script:name?a=", "name", true, { { "a", "" }, { "A", nullptr } } }, + { "vnd.sun.star.script:name?a=&", nullptr, true, {} }, + { "vnd.sun.star.script:name?key1=&%26=%3D&key1=hello", "name", true, + { { "key1", "" }, { "key2", nullptr }, { "&", "=" } } } }; + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + css::uno::Reference< css::uri::XUriReference > uriRef( + m_uriFactory->parse( + OUString::createFromAscii(data[i].uriReference))); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", i, data[i].uriReference, data[i].name != nullptr, + uriRef.is()); + if (uriRef.is()) { + css::uno::Reference< css::uri::XVndSunStarScriptUrlReference > + scriptUrl(uriRef, css::uno::UNO_QUERY_THROW); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", i, data[i].uriReference, + OUString::createFromAscii(data[i].uriReference), + scriptUrl->getUriReference()); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", i, data[i].uriReference, + OUString::createFromAscii(data[i].name), + scriptUrl->getName()); + OUString originalReference(uriRef->getUriReference()); + for (std::size_t j = 0; j < parameterCount; ++j) { + if (data[i].parameters[j].key != nullptr) { + TEST_ASSERT_EQUAL( + "testVndSunStarScript", + static_cast< double >(i) + + static_cast< double >(j) / 10.0, + data[i].uriReference, + data[i].parameters[j].value != nullptr, + bool( + scriptUrl->hasParameter( + OUString::createFromAscii( + data[i].parameters[j].key)))); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", + static_cast< double >(i) + + static_cast< double >(j) / 10.0, + data[i].uriReference, + OUString::createFromAscii( + data[i].parameters[j].value), + scriptUrl->getParameter( + OUString::createFromAscii( + data[i].parameters[j].key))); + + // setting the parameter to its original value should not change + // the overall uri reference (provided it was normalized before) + if ( data[i].normalized ) { + if ( scriptUrl->hasParameter(OUString::createFromAscii( + data[i].parameters[j].key)) ) { + scriptUrl->setParameter( + OUString::createFromAscii( + data[i].parameters[j].key), + scriptUrl->getParameter( + OUString::createFromAscii( + data[i].parameters[j].key))); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", + static_cast< double >(i) + + static_cast< double >(j) / 10.0, + OUString("setParameter"), + originalReference, + uriRef->getUriReference()); + } + } + } + } + if ( data[i].normalized ) { + scriptUrl->setName(scriptUrl->getName()); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", + i, + OUString("setName"), + originalReference, + uriRef->getUriReference()); + } + } + } + + css::uno::Reference< css::uri::XUriReference > uriRef( + m_uriFactory->parse( + "vnd.sun.star.script:Hello?location=Library.Module"), + css::uno::UNO_SET_THROW); + css::uno::Reference< css::uri::XVndSunStarScriptUrlReference > + scriptUrl(uriRef, css::uno::UNO_QUERY_THROW); + + scriptUrl->setParameter( + "location", + "foo"); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", sal_Int32(10), sal_Int32(1), + OUString("vnd.sun.star.script:Hello?location=foo"), + uriRef->getUriReference()); + + scriptUrl->setParameter( + "language", + "StarBasic"); + TEST_ASSERT_EQUAL( + "testVndSunStarScript", sal_Int32(10), sal_Int32(2), + OUString("vnd.sun.star.script:Hello?location=foo&language=StarBasic"), + uriRef->getUriReference()); + + + bool caughtExpected = false; + try { + scriptUrl->setName(OUString()); + } + catch( const css::lang::IllegalArgumentException& ) { + caughtExpected = true; + } + TEST_ASSERT_EQUAL( + "testVndSunStarScript", + OUString("illegal arguments"), + OUString("name"), + true, + caughtExpected); + + caughtExpected = false; + try { + scriptUrl->setParameter( + OUString(), + "non-empty"); + } + catch( const css::lang::IllegalArgumentException& ) { + caughtExpected = true; + } + TEST_ASSERT_EQUAL( + "testVndSunStarScript", + OUString("illegal arguments"), + OUString("parameter"), + true, + caughtExpected); +} + +void Test::testTranslator() { + struct Data { + char const * externalUriReference; + char const * internalUriReference; + bool toInternal; + }; + Data data[] = { + { "", "", true }, + { "#fragment", "#fragment", true }, + { "segment/segment?query#fragment", "segment/segment?query#fragment", + true }, + { "/segment/segment?query#fragment", "/segment/segment?query#fragment", + true }, + { "//authority/segment?query#fragment", + "//authority/segment?query#fragment", true }, + { "foo:bar#fragment", "foo:bar#fragment", true }, + { "file:///abc/def", "file:///abc/def", true }, + { "file:///abc/%FEef", "file:///abc/%feef", false }, + { "file:///abc/%80%80ef", "file:///abc/%80%80ef", false }, + { "file:///abc/%ED%A0%80%ED%B0%80ef", + "file:///abc/%ED%A0%80%ED%B0%80ef", false }, + { "file:///abc/%25.ef", "file:///abc/%.ef", false }, + { "file:///abc/%25ef", "file:///abc/%25ef", true } }; + css::uno::Reference< css::uri::XExternalUriReferenceTranslator > + translator(css::uri::ExternalUriReferenceTranslator::create(m_context)); + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + if (data[i].toInternal) { + TEST_ASSERT_EQUAL( + "testTranslator, translateToInternal", i, + data[i].externalUriReference, + OUString::createFromAscii(data[i].internalUriReference), + translator->translateToInternal( + OUString::createFromAscii( + data[i].externalUriReference))); + } + TEST_ASSERT_EQUAL( + "testTranslator, translateToExternal", i, + data[i].internalUriReference, + OUString::createFromAscii(data[i].externalUriReference), + translator->translateToExternal( + OUString::createFromAscii(data[i].internalUriReference))); + } +} + +void Test::testPkgUrlFactory() { + struct Data { + char const * authority; + char const * result; + }; + Data data[] = { + { "a/b/c", nullptr }, + { "file:///#foo", nullptr }, + { "file:///a%25b%2fc/d~e&f@g?h", + "vnd.sun.star.pkg://file:%2F%2F%2Fa%2525b%252fc%2Fd~e&f@g%3Fh" } }; + css::uno::Reference< css::uri::XVndSunStarPkgUrlReferenceFactory > factory( + css::uri::VndSunStarPkgUrlReferenceFactory::create(m_context)); + for (std::size_t i = 0; i < SAL_N_ELEMENTS(data); ++i) { + css::uno::Reference< css::uri::XUriReference > url( + factory->createVndSunStarPkgUrlReference( + m_uriFactory->parse( + OUString::createFromAscii(data[i].authority)))); + TEST_ASSERT_EQUAL( + "testVndSunStarPkgFactory", i, data[i].authority, + data[i].result != nullptr, url.is()); + if (data[i].result != nullptr) { + TEST_ASSERT_EQUAL( + "testVndSunStarPkgFactory", i, data[i].authority, + OUString::createFromAscii(data[i].result), + url->getUriReference()); + } + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Test); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/unosdk.mk b/stoc/unosdk.mk new file mode 100644 index 0000000000..b28206536e --- /dev/null +++ b/stoc/unosdk.mk @@ -0,0 +1,25 @@ +# +# This file is part of the LibreOffice project. +# +# 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 file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +PPATH=inc + +FILES= \ + $(PPATH)\stoc\component.hxx + +unosdk: $(FILES) + docpp -H -m -f -u -d ..\..\doc\stoc $(FILES) diff --git a/stoc/util/bootstrap.component b/stoc/util/bootstrap.component new file mode 100644 index 0000000000..b6d5a7b810 --- /dev/null +++ b/stoc/util/bootstrap.component @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.DLLComponentLoader" + constructor="com_sun_star_comp_stoc_DLLComponentLoader_get_implementation"> + <service name="com.sun.star.loader.SharedLibrary"/> + </implementation> + <implementation name="com.sun.star.comp.stoc.ImplementationRegistration" + constructor="com_sun_star_comp_stoc_ImplementationRegistration_get_implementation"> + <service name="com.sun.star.registry.ImplementationRegistration"/> + </implementation> + <implementation name="com.sun.star.comp.stoc.NestedRegistry" + constructor="com_sun_star_comp_stoc_NestedRegistry_get_implementation"> + <service name="com.sun.star.registry.NestedRegistry"/> + </implementation> + <implementation name="com.sun.star.comp.stoc.ORegistryServiceManager" + constructor="com_sun_star_comp_stoc_ORegistryServiceManager_get_implementation"> + <service name="com.sun.star.lang.RegistryServiceManager"/> + </implementation> + <implementation name="com.sun.star.comp.stoc.OServiceManager" + constructor="com_sun_star_comp_stoc_OServiceManager_get_implementation"> + <service name="com.sun.star.lang.ServiceManager"/> + </implementation> + <implementation name="com.sun.star.comp.stoc.OServiceManagerWrapper" + constructor="com_sun_star_comp_stoc_OServiceManagerWrapper_get_implementation"/> + <implementation name="com.sun.star.comp.stoc.SimpleRegistry" + constructor="com_sun_star_comp_stoc_SimpleRegistry_get_implementation"> + <service name="com.sun.star.registry.SimpleRegistry"/> + </implementation> + <implementation name="com.sun.star.security.comp.stoc.AccessController" + constructor="com_sun_star_security_comp_stoc_AccessController_get_implementation"> + <service name="com.sun.star.security.AccessController"/> + </implementation> + <implementation name="com.sun.star.security.comp.stoc.FilePolicy" + constructor="com_sun_star_security_comp_stoc_FilePolicy_get_implementation"> + <service name="com.sun.star.security.Policy"/> + </implementation> +</component> diff --git a/stoc/util/stocservices.component b/stoc/util/stocservices.component new file mode 100644 index 0000000000..bbb113f0d8 --- /dev/null +++ b/stoc/util/stocservices.component @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * 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 file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.TypeConverter" + constructor="com_sun_star_comp_stoc_TypeConverter_get_implementation"> + <service name="com.sun.star.script.Converter"/> + </implementation> + <implementation name="com.sun.star.comp.uri.ExternalUriReferenceTranslator" + constructor="com_sun_star_comp_uri_ExternalUriReferenceTranslator_get_implementation"> + <service name="com.sun.star.uri.ExternalUriReferenceTranslator"/> + </implementation> + <implementation name="com.sun.star.comp.uri.UriReferenceFactory" + constructor="com_sun_star_comp_uri_UriReferenceFactory_get_implementation"> + <service name="com.sun.star.uri.UriReferenceFactory"/> + </implementation> + <implementation name="com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTexpand" + constructor="com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTexpand_get_implementation"> + <service name="com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTexpand"/> + </implementation> + <implementation name="com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript" + constructor="com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTscript_get_implementation"> + <service name="com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"/> + </implementation> + <implementation name="com.sun.star.comp.uri.VndSunStarPkgUrlReferenceFactory" + constructor="com_sun_star_comp_uri_VndSunStarPkgUrlReferenceFactory_get_implementation"> + <service name="com.sun.star.uri.VndSunStarPkgUrlReferenceFactory"/> + </implementation> +</component> |