diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /scripting | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/4%7.4.7.tar.xz libreoffice-upstream/4%7.4.7.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scripting')
200 files changed, 29646 insertions, 0 deletions
diff --git a/scripting/Format_java_code.sh b/scripting/Format_java_code.sh new file mode 100755 index 000000000..bb8e40dd2 --- /dev/null +++ b/scripting/Format_java_code.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +astyle --options=astyle.options --verbose --recursive ./*.java diff --git a/scripting/IwyuFilter_scripting.yaml b/scripting/IwyuFilter_scripting.yaml new file mode 100644 index 000000000..18b80ff99 --- /dev/null +++ b/scripting/IwyuFilter_scripting.yaml @@ -0,0 +1,6 @@ +--- +assumeFilename: scripting/source/stringresource/stringresource.cxx +excludelist: + scripting/source/protocolhandler/scripthandler.cxx: + # Actually used + - com/sun/star/lang/XSingleServiceFactory.hpp diff --git a/scripting/Jar_HelloWorld.mk b/scripting/Jar_HelloWorld.mk new file mode 100644 index 000000000..1a44592e1 --- /dev/null +++ b/scripting/Jar_HelloWorld.mk @@ -0,0 +1,22 @@ +# -*- 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_Jar_Jar,HelloWorld)) + +$(eval $(call gb_Jar_use_jars,HelloWorld,\ + libreoffice \ +)) + +$(eval $(call gb_Jar_add_sourcefiles,HelloWorld,\ + scripting/examples/java/HelloWorld/HelloWorld \ +)) + +$(eval $(call gb_Jar_set_packageroot,HelloWorld,org)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Jar_Highlight.mk b/scripting/Jar_Highlight.mk new file mode 100644 index 000000000..fb756cc8b --- /dev/null +++ b/scripting/Jar_Highlight.mk @@ -0,0 +1,22 @@ +# -*- 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_Jar_Jar,Highlight)) + +$(eval $(call gb_Jar_use_jars,Highlight,\ + libreoffice \ +)) + +$(eval $(call gb_Jar_add_sourcefiles,Highlight,\ + scripting/examples/java/Highlight/HighlightText \ +)) + +$(eval $(call gb_Jar_set_packageroot,Highlight,org)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Jar_MemoryUsage.mk b/scripting/Jar_MemoryUsage.mk new file mode 100644 index 000000000..8f917d360 --- /dev/null +++ b/scripting/Jar_MemoryUsage.mk @@ -0,0 +1,22 @@ +# -*- 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_Jar_Jar,MemoryUsage)) + +$(eval $(call gb_Jar_use_jars,MemoryUsage,\ + libreoffice \ +)) + +$(eval $(call gb_Jar_add_sourcefiles,MemoryUsage,\ + scripting/examples/java/MemoryUsage/MemoryUsage \ +)) + +$(eval $(call gb_Jar_set_packageroot,MemoryUsage,org)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Jar_ScriptFramework.mk b/scripting/Jar_ScriptFramework.mk new file mode 100644 index 000000000..5c3b20776 --- /dev/null +++ b/scripting/Jar_ScriptFramework.mk @@ -0,0 +1,55 @@ +# -*- 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_Jar_Jar,ScriptFramework)) + +$(eval $(call gb_Jar_use_jars,ScriptFramework,\ + libreoffice \ +)) + +$(eval $(call gb_Jar_set_componentfile,ScriptFramework,scripting/java/ScriptFramework,OOO,services)) + +$(eval $(call gb_Jar_set_manifest,ScriptFramework,$(SRCDIR)/scripting/java/Framework/MANIFEST.MF)) + +$(eval $(call gb_Jar_set_packageroot,ScriptFramework,com)) + +$(eval $(call gb_Jar_add_sourcefiles,ScriptFramework,\ + scripting/java/com/sun/star/script/framework/browse/DialogFactory \ + scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode \ + scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode \ + scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode \ + scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode \ + scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB \ + scripting/java/com/sun/star/script/framework/container/ParcelContainer \ + scripting/java/com/sun/star/script/framework/container/ParcelDescriptor \ + scripting/java/com/sun/star/script/framework/container/Parcel \ + scripting/java/com/sun/star/script/framework/container/ParsedScriptUri \ + scripting/java/com/sun/star/script/framework/container/ScriptEntry \ + scripting/java/com/sun/star/script/framework/container/ScriptMetaData \ + scripting/java/com/sun/star/script/framework/container/UnoPkgContainer \ + scripting/java/com/sun/star/script/framework/container/XMLParserFactory \ + scripting/java/com/sun/star/script/framework/container/XMLParser \ + scripting/java/com/sun/star/script/framework/io/UCBStreamHandler \ + scripting/java/com/sun/star/script/framework/io/XInputStreamImpl \ + scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper \ + scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper \ + scripting/java/com/sun/star/script/framework/io/XStorageHelper \ + scripting/java/com/sun/star/script/framework/log/LogUtils \ + scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory \ + scripting/java/com/sun/star/script/framework/provider/EditorScriptContext \ + scripting/java/com/sun/star/script/framework/provider/PathUtils \ + scripting/java/com/sun/star/script/framework/provider/ScriptContext \ + scripting/java/com/sun/star/script/framework/provider/ScriptEditor \ + scripting/java/com/sun/star/script/framework/provider/ScriptEditorBase \ + scripting/java/com/sun/star/script/framework/provider/ScriptProvider \ + scripting/java/com/sun/star/script/framework/provider/SwingInvocation \ + scripting/java/Framework/com/sun/star/script/framework/security/SecurityDialog \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Jar_ScriptProviderForBeanShell.mk b/scripting/Jar_ScriptProviderForBeanShell.mk new file mode 100644 index 000000000..0d1535d07 --- /dev/null +++ b/scripting/Jar_ScriptProviderForBeanShell.mk @@ -0,0 +1,41 @@ +# -*- 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_Jar_Jar,ScriptProviderForBeanShell)) + +$(eval $(call gb_Jar_use_jars,ScriptProviderForBeanShell,\ + libreoffice \ + ScriptFramework \ +)) + +$(eval $(call gb_Jar_use_externals,ScriptProviderForBeanShell,\ + bsh \ +)) + +$(eval $(call gb_Jar_set_manifest,ScriptProviderForBeanShell,$(SRCDIR)/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF)) + +$(eval $(call gb_Jar_set_componentfile,ScriptProviderForBeanShell,scripting/java/ScriptProviderForBeanShell,OOO,scriptproviderforbeanshell)) + +$(eval $(call gb_Jar_set_packageroot,ScriptProviderForBeanShell,com)) + +$(eval $(call gb_Jar_add_sourcefiles,ScriptProviderForBeanShell,\ + scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView \ + scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell \ + scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell \ + scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel \ + scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView \ + scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener \ +)) + +$(eval $(call gb_Jar_add_packagefile,ScriptProviderForBeanShell,\ + com/sun/star/script/framework/provider/beanshell/template.bsh,\ + $(SRCDIR)/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Jar_ScriptProviderForJava.mk b/scripting/Jar_ScriptProviderForJava.mk new file mode 100644 index 000000000..d87310d9c --- /dev/null +++ b/scripting/Jar_ScriptProviderForJava.mk @@ -0,0 +1,31 @@ +# -*- 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_Jar_Jar,ScriptProviderForJava)) + +$(eval $(call gb_Jar_use_jars,ScriptProviderForJava,\ + libreoffice \ + ScriptFramework \ +)) + +$(eval $(call gb_Jar_set_componentfile,ScriptProviderForJava,scripting/java/ScriptProviderForJava,OOO,services)) + +$(eval $(call gb_Jar_set_manifest,ScriptProviderForJava,$(SRCDIR)/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF)) + +$(eval $(call gb_Jar_set_packageroot,ScriptProviderForJava,com)) + +$(eval $(call gb_Jar_add_sourcefiles,ScriptProviderForJava,\ + scripting/java/com/sun/star/script/framework/provider/java/Resolver \ + scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor \ + scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava \ + scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy \ + scripting/java/com/sun/star/script/framework/provider/java/StrictResolver \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Jar_ScriptProviderForJavaScript.mk b/scripting/Jar_ScriptProviderForJavaScript.mk new file mode 100644 index 000000000..0366016bb --- /dev/null +++ b/scripting/Jar_ScriptProviderForJavaScript.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_Jar_Jar,ScriptProviderForJavaScript)) + +$(eval $(call gb_Jar_use_jars,ScriptProviderForJavaScript,\ + libreoffice \ + ScriptFramework \ +)) + +$(eval $(call gb_Jar_use_externals,ScriptProviderForJavaScript,\ + rhino \ +)) + +$(eval $(call gb_Jar_set_manifest,ScriptProviderForJavaScript,$(SRCDIR)/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF)) + +$(eval $(call gb_Jar_set_componentfile,ScriptProviderForJavaScript,scripting/java/ScriptProviderForJavaScript,OOO,scriptproviderforjavascript)) + +$(eval $(call gb_Jar_set_packageroot,ScriptProviderForJavaScript,com)) + +$(eval $(call gb_Jar_add_sourcefiles,ScriptProviderForJavaScript,\ + scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript \ + scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript \ +)) + +$(eval $(call gb_Jar_add_packagefile,ScriptProviderForJavaScript,\ + com/sun/star/script/framework/provider/javascript/template.js,\ + $(SRCDIR)/scripting/java/com/sun/star/script/framework/provider/javascript/template.js \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Library_basprov.mk b/scripting/Library_basprov.mk new file mode 100644 index 000000000..417068001 --- /dev/null +++ b/scripting/Library_basprov.mk @@ -0,0 +1,44 @@ +# -*- 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,basprov)) + +$(eval $(call gb_Library_set_componentfile,basprov,scripting/source/basprov/basprov,services)) + +$(eval $(call gb_Library_set_include,basprov,\ + $$(INCLUDE) \ + -I$(SRCDIR)/scripting/source/inc \ +)) + +$(eval $(call gb_Library_use_external,basprov,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,basprov)) + +$(eval $(call gb_Library_use_libraries,basprov,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + sb \ + sfx \ + svl \ + tl \ + ucbhelper \ + vcl \ +)) + +$(eval $(call gb_Library_add_exception_objects,basprov,\ + scripting/source/basprov/baslibnode \ + scripting/source/basprov/basmethnode \ + scripting/source/basprov/basmodnode \ + scripting/source/basprov/basprov \ + scripting/source/basprov/basscript \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Library_dlgprov.mk b/scripting/Library_dlgprov.mk new file mode 100644 index 000000000..5f53a9f30 --- /dev/null +++ b/scripting/Library_dlgprov.mk @@ -0,0 +1,49 @@ +# -*- 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,dlgprov)) + +$(eval $(call gb_Library_set_componentfile,dlgprov,scripting/source/dlgprov/dlgprov,services)) + +$(eval $(call gb_Library_set_include,dlgprov,\ + $$(INCLUDE) \ + -I$(SRCDIR)/scripting/source/inc \ +)) + +$(eval $(call gb_Library_use_external,dlgprov,boost_headers)) + +$(eval $(call gb_Library_use_api,dlgprov,\ + offapi \ + oovbaapi \ + udkapi \ +)) + +$(eval $(call gb_Library_use_libraries,dlgprov,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + i18nlangtag \ + sb \ + sfx \ + tl \ + ucbhelper \ + utl \ + vbahelper \ + vcl \ + xmlscript \ +)) + +$(eval $(call gb_Library_add_exception_objects,dlgprov,\ + scripting/source/dlgprov/DialogModelProvider \ + scripting/source/dlgprov/dlgevtatt \ + scripting/source/dlgprov/dlgprov \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Library_protocolhandler.mk b/scripting/Library_protocolhandler.mk new file mode 100644 index 000000000..31599c3d8 --- /dev/null +++ b/scripting/Library_protocolhandler.mk @@ -0,0 +1,42 @@ +# -*- 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,protocolhandler)) + +$(eval $(call gb_Library_set_componentfile,protocolhandler,scripting/source/protocolhandler/protocolhandler,services)) + +$(eval $(call gb_Library_set_include,protocolhandler,\ + $$(INCLUDE) \ + -I$(SRCDIR)/scripting/source/inc \ +)) + +$(eval $(call gb_Library_use_external,protocolhandler,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,protocolhandler)) + +$(eval $(call gb_Library_use_custom_headers,protocolhandler,\ + officecfg/registry \ +)) + +$(eval $(call gb_Library_use_libraries,protocolhandler,\ + comphelper \ + cppu \ + cppuhelper \ + fwk \ + sal \ + sfx \ + tl \ + vcl \ +)) + +$(eval $(call gb_Library_add_exception_objects,protocolhandler,\ + scripting/source/protocolhandler/scripthandler \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Library_scriptframe.mk b/scripting/Library_scriptframe.mk new file mode 100644 index 000000000..2cf22376e --- /dev/null +++ b/scripting/Library_scriptframe.mk @@ -0,0 +1,42 @@ +# -*- 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,scriptframe)) + +$(eval $(call gb_Library_set_componentfile,scriptframe,scripting/util/scriptframe,services)) + +$(eval $(call gb_Library_set_include,scriptframe,\ + $$(INCLUDE) \ + -I$(SRCDIR)/scripting/source/inc \ +)) + +$(eval $(call gb_Library_use_external,scriptframe,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,scriptframe)) + +$(eval $(call gb_Library_use_libraries,scriptframe,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + tl \ + ucbhelper \ + utl \ +)) + +$(eval $(call gb_Library_add_exception_objects,scriptframe,\ + scripting/source/provider/ActiveMSPList \ + scripting/source/provider/BrowseNodeFactoryImpl \ + scripting/source/provider/MasterScriptProvider \ + scripting/source/provider/MasterScriptProviderFactory \ + scripting/source/provider/ProviderCache \ + scripting/source/provider/URIHelper \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Library_stringresource.mk b/scripting/Library_stringresource.mk new file mode 100644 index 000000000..fa9055421 --- /dev/null +++ b/scripting/Library_stringresource.mk @@ -0,0 +1,36 @@ +# -*- 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,stringresource)) + +$(eval $(call gb_Library_set_componentfile,stringresource,scripting/source/stringresource/stringresource,services)) + +$(eval $(call gb_Library_set_include,stringresource,\ + $$(INCLUDE) \ + -I$(SRCDIR)/scripting/source/inc \ +)) + +$(eval $(call gb_Library_use_external,stringresource,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,stringresource)) + +$(eval $(call gb_Library_use_libraries,stringresource,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + tl \ + i18nlangtag \ +)) + +$(eval $(call gb_Library_add_exception_objects,stringresource,\ + scripting/source/stringresource/stringresource \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Library_vbaevents.mk b/scripting/Library_vbaevents.mk new file mode 100644 index 000000000..2990a892a --- /dev/null +++ b/scripting/Library_vbaevents.mk @@ -0,0 +1,42 @@ +# -*- 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,vbaevents)) + +$(eval $(call gb_Library_set_componentfile,vbaevents,scripting/source/vbaevents/vbaevents,services)) + +$(eval $(call gb_Library_set_include,vbaevents,\ + $$(INCLUDE) \ + -I$(SRCDIR)/scripting/source/inc \ +)) + +$(eval $(call gb_Library_use_external,vbaevents,boost_headers)) + +$(eval $(call gb_Library_use_api,vbaevents,\ + offapi \ + oovbaapi \ + udkapi \ +)) + +$(eval $(call gb_Library_use_libraries,vbaevents,\ + comphelper \ + cppu \ + cppuhelper \ + msfilter \ + sal \ + sfx \ + sb \ + tl \ +)) + +$(eval $(call gb_Library_add_exception_objects,vbaevents,\ + scripting/source/vbaevents/eventhelper \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Makefile b/scripting/Makefile new file mode 100644 index 000000000..0997e6284 --- /dev/null +++ b/scripting/Makefile @@ -0,0 +1,14 @@ +# -*- 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/. +# + +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/scripting/Module_scripting.mk b/scripting/Module_scripting.mk new file mode 100644 index 000000000..1d974e8e7 --- /dev/null +++ b/scripting/Module_scripting.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_Module_Module,scripting)) + +ifneq ($(filter SCRIPTING,$(BUILD_TYPE)),) + +$(eval $(call gb_Module_add_targets,scripting,\ + $(if $(ENABLE_JAVA),\ + Jar_HelloWorld \ + Jar_Highlight \ + Jar_MemoryUsage \ + Jar_ScriptFramework \ + $(if $(ENABLE_SCRIPTING_BEANSHELL),\ + Jar_ScriptProviderForBeanShell \ + Rdb_scriptproviderforbeanshell \ + ) \ + Jar_ScriptProviderForJava \ + $(if $(ENABLE_SCRIPTING_JAVASCRIPT),\ + Jar_ScriptProviderForJavaScript \ + Rdb_scriptproviderforjavascript \ + ) \ + Package_java \ + Package_java_jars \ + $(if $(ENABLE_SCRIPTING_BEANSHELL),Package_ScriptsBeanShell) \ + $(if $(ENABLE_SCRIPTING_JAVASCRIPT),Package_ScriptsJavaScript) \ + ) \ + Package_scriptbindinglib \ + $(if $(DISABLE_PYTHON),,\ + Package_scriptproviderforpython \ + Package_ScriptsPython \ + Pyuno_mailmerge \ + ) \ + Library_basprov \ + Library_dlgprov \ + Library_protocolhandler \ + Library_scriptframe \ + Library_stringresource \ + Library_vbaevents \ +)) + +endif + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_ScriptsBeanShell.mk b/scripting/Package_ScriptsBeanShell.mk new file mode 100644 index 000000000..5b7cf42d3 --- /dev/null +++ b/scripting/Package_ScriptsBeanShell.mk @@ -0,0 +1,39 @@ +# -*- 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_Package_Package,scripting_ScriptsBeanShell,$(SRCDIR)/scripting/examples)) + +$(eval $(call gb_Package_add_files_with_dir,scripting_ScriptsBeanShell,$(LIBO_SHARE_FOLDER)/Scripts,\ + beanshell/Calc/CopyRange.bsh \ + beanshell/Calc/FixView.bsh \ + beanshell/Calc/InsertSheet.bsh \ + beanshell/Calc/parcel-descriptor.xml \ + beanshell/Calc/ProtectSheet.bsh \ + beanshell/Calc/SelectCell.bsh \ + beanshell/Capitalise/capitalise.bsh \ + beanshell/Capitalise/parcel-descriptor.xml \ + beanshell/HelloWorld/helloworld.bsh \ + beanshell/HelloWorld/parcel-descriptor.xml \ + beanshell/Highlight/ButtonPressHandler.bsh \ + beanshell/Highlight/highlighter.bsh \ + beanshell/Highlight/parcel-descriptor.xml \ + beanshell/Highlight/ShowDialog.bsh \ + beanshell/MemoryUsage/memusage.bsh \ + beanshell/MemoryUsage/parcel-descriptor.xml \ + beanshell/WordCount/parcel-descriptor.xml \ + beanshell/WordCount/wordcount.bsh \ + beanshell/Writer/ChangeFont.bsh \ + beanshell/Writer/ChangeParaAdjust.bsh \ + beanshell/Writer/InsertTable.bsh \ + beanshell/Writer/InsertText.bsh \ + beanshell/Writer/parcel-descriptor.xml \ + beanshell/Writer/SetText.bsh \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_ScriptsJavaScript.mk b/scripting/Package_ScriptsJavaScript.mk new file mode 100644 index 000000000..df6ee2a25 --- /dev/null +++ b/scripting/Package_ScriptsJavaScript.mk @@ -0,0 +1,22 @@ +# -*- 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_Package_Package,scripting_ScriptsJavaScript,$(SRCDIR)/scripting/examples)) + +$(eval $(call gb_Package_add_files_with_dir,scripting_ScriptsJavaScript,$(LIBO_SHARE_FOLDER)/Scripts,\ + javascript/ExportSheetsToHTML/exportsheetstohtml.js \ + javascript/ExportSheetsToHTML/parcel-descriptor.xml \ + javascript/HelloWorld/helloworld.js \ + javascript/HelloWorld/parcel-descriptor.xml \ + javascript/Highlight/ButtonPressHandler.js \ + javascript/Highlight/parcel-descriptor.xml \ + javascript/Highlight/ShowDialog.js \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_ScriptsPython.mk b/scripting/Package_ScriptsPython.mk new file mode 100644 index 000000000..9d401d383 --- /dev/null +++ b/scripting/Package_ScriptsPython.mk @@ -0,0 +1,21 @@ +# -*- 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_Package_Package,scripting_ScriptsPython,$(SRCDIR)/scripting/examples)) + +$(eval $(call gb_Package_add_files_with_dir,scripting_ScriptsPython,$(LIBO_SHARE_FOLDER)/Scripts,\ + python/Capitalise.py \ + python/HelloWorld.py \ + python/InsertText.py \ + python/NamedRanges.py \ + python/SetCellColor.py \ + python/TableSample.py \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_java.mk b/scripting/Package_java.mk new file mode 100644 index 000000000..bbaa6e2b7 --- /dev/null +++ b/scripting/Package_java.mk @@ -0,0 +1,21 @@ +# -*- 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_Package_Package,scripting_java,$(SRCDIR)/scripting/examples/java)) + +$(eval $(call gb_Package_add_files_with_dir,scripting_java,$(LIBO_SHARE_FOLDER)/Scripts/java,\ + HelloWorld/HelloWorld.java \ + HelloWorld/parcel-descriptor.xml \ + Highlight/HighlightText.java \ + Highlight/parcel-descriptor.xml \ + MemoryUsage/MemoryUsage.java \ + MemoryUsage/parcel-descriptor.xml \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_java_jars.mk b/scripting/Package_java_jars.mk new file mode 100644 index 000000000..13ba07e8c --- /dev/null +++ b/scripting/Package_java_jars.mk @@ -0,0 +1,16 @@ +# -*- 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_Package_Package,scripting_java_jars,$(WORKDIR)/Jar)) + +$(eval $(call gb_Package_add_file,scripting_java_jars,$(LIBO_SHARE_FOLDER)/Scripts/java/HelloWorld/HelloWorld.jar,HelloWorld.jar)) +$(eval $(call gb_Package_add_file,scripting_java_jars,$(LIBO_SHARE_FOLDER)/Scripts/java/Highlight/Highlight.jar,Highlight.jar)) +$(eval $(call gb_Package_add_file,scripting_java_jars,$(LIBO_SHARE_FOLDER)/Scripts/java/MemoryUsage/MemoryUsage.jar,MemoryUsage.jar)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_scriptbindinglib.mk b/scripting/Package_scriptbindinglib.mk new file mode 100644 index 000000000..54ecbb2cc --- /dev/null +++ b/scripting/Package_scriptbindinglib.mk @@ -0,0 +1,18 @@ +# -*- 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_Package_Package,scripting_scriptbindinglib,$(SRCDIR)/scripting/workben/bindings)) + +$(eval $(call gb_Package_add_files,scripting_scriptbindinglib,$(LIBO_SHARE_FOLDER)/basic/ScriptBindingLibrary,\ + Highlight.xdl \ + dialog.xlb \ + script.xlb \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Package_scriptproviderforpython.mk b/scripting/Package_scriptproviderforpython.mk new file mode 100644 index 000000000..7e2143998 --- /dev/null +++ b/scripting/Package_scriptproviderforpython.mk @@ -0,0 +1,15 @@ +# -*- 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_Package_Package,scripting_scriptproviderforpython,$(SRCDIR)/scripting/source/pyprov)) + +$(eval $(call gb_Package_add_file,scripting_scriptproviderforpython,$(LIBO_ETC_FOLDER)/services/scriptproviderforpython.rdb,scriptproviderforpython.rdb)) +$(eval $(call gb_Package_add_file,scripting_scriptproviderforpython,$(LIBO_LIB_PYUNO_FOLDER)/pythonscript.py,pythonscript.py)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Pyuno_mailmerge.mk b/scripting/Pyuno_mailmerge.mk new file mode 100644 index 000000000..f5cce752c --- /dev/null +++ b/scripting/Pyuno_mailmerge.mk @@ -0,0 +1,19 @@ +# -*- 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_Pyuno_Pyuno,mailmerge,$(SRCDIR)/scripting/source/pyprov)) + +$(eval $(call gb_Pyuno_set_componentfile,mailmerge,scripting/source/pyprov/mailmerge,services)) + +$(eval $(call gb_Pyuno_add_files,mailmerge,,\ + mailmerge.py \ + msgbox.py \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/README.md b/scripting/README.md new file mode 100644 index 000000000..0560a73b9 --- /dev/null +++ b/scripting/README.md @@ -0,0 +1,70 @@ +# Scripting Framework + +This module provides the source code for the Scripting Framework. + +For more information on the Scripting Framework, see the project web page: +<https://framework.openoffice.org/scripting/> + +This module uses astyle to keep consistent java coding style. Please run + + ./Format_java_code.sh + +before committing. + +## Source Code Structure + +The following directories contain the source code currently used +by the Scripting Framework: + +- `source/provider` + +C++ source for the implementations of the `com.sun.star.script.provider.*` +and `com.sun.star.script.browse.*` UNO types. These types are used for +browsing and executing scripts. + +- `source/protocolhandler` + +C++ for a `ProtocolHandler` implementation that handles `vnd.sun.star.script` +URIs and dispatches them for execution to the Scripting Framework. + +- `source/basprov` + +C++ implementation of the `LanguageScriptProvider` UNO service for Basic + +- `source/dlgprov` + +C++ implementation of the `DialogProvider` UNO service used for loading +UNO dialogs from various languages + +- `source/pyprov` + +`LanguageScriptProvider` for Python + +- `java/com/sun/star/script/framework/provider` + +Implementation of an abstract base class ScriptProvider which provides +core methods for implementing Java based `LanguageScriptProvider` implementations + +- `java/com/sun/star/script/framework/provider/*` + +`BeanShell`, JavaScript and Java `LanguageScriptProvider` implementations + +- `java/com/sun/star/script/framework/browse/*` + +`BrowseNode` implementations for the Java based `LanguageScriptProviders` + +- `java/com/sun/star/script/framework/io` +- `java/com/sun/star/script/framework/container` + +Classes for performing script IO + +- `examples` + +Example scripts in BeanShell, JavaScript, Java and Python + + +## Deprecated Code + +- `java/org/openoffice/*` + +Support for developing scripts in IDEs such as NetBeans. diff --git a/scripting/Rdb_scriptproviderforbeanshell.mk b/scripting/Rdb_scriptproviderforbeanshell.mk new file mode 100644 index 000000000..5b19a1523 --- /dev/null +++ b/scripting/Rdb_scriptproviderforbeanshell.mk @@ -0,0 +1,12 @@ +# -*- 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_Rdb_Rdb_install,scriptproviderforbeanshell)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/Rdb_scriptproviderforjavascript.mk b/scripting/Rdb_scriptproviderforjavascript.mk new file mode 100644 index 000000000..06db18fa1 --- /dev/null +++ b/scripting/Rdb_scriptproviderforjavascript.mk @@ -0,0 +1,12 @@ +# -*- 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_Rdb_Rdb_install,scriptproviderforjavascript)) + +# vim: set noet sw=4 ts=4: diff --git a/scripting/astyle.options b/scripting/astyle.options new file mode 100644 index 000000000..e8b338807 --- /dev/null +++ b/scripting/astyle.options @@ -0,0 +1,19 @@ +--style=java +--mode=java +--indent-switches +--indent-cases +--min-conditional-indent=0 +--max-instatement-indent=40 +--break-blocks +--unpad-paren +--align-reference=name +--pad-oper +--pad-header +-s4 +--indent-col1-comments +--lineend=linux +--ignore-exclude-errors +--suffix=none +--preserve-date +--formatted +--max-code-length=80 diff --git a/scripting/examples/basic/InsertColouredText.xba b/scripting/examples/basic/InsertColouredText.xba new file mode 100644 index 000000000..e97b5dfd8 --- /dev/null +++ b/scripting/examples/basic/InsertColouredText.xba @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd"> +<!-- + * 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 . +--> +<script:module xmlns:script="http://openoffice.org/2000/script" script:name="InsertColouredText" script:language="StarBasic">' *** +' InsertColouredText basic script +' Uses a user interface to insert text of a specified colour to the +' start and end of a document +' +' author Neil Montgomery +' created August 12, 2002 +' *** + + +' Main subprocedure to start script +Sub Main + dialogShow() +End Sub + + +' Global reference to the dialog object +Dim oDialog as Object + + +' Uses the loadDialog subprocedure to load and execute the dialog box +Sub dialogShow + oDialog = loadDialog("Standard","InsertColouredTextDialog") + oDialog.execute() +End Sub + + +' *** +' Loads the dialog from the dialog library +' +' param Libname the library name where dialog is stored +' param DialogName the name of the dialog +' param oLibContainer library container to hold the loaded dialog library (optional) +' return runtime dialog object +' *** +Function loadDialog(Libname as String, DialogName as String, Optional oLibContainer) + Dim oLib as Object + Dim oLibDialog as Object + Dim oRuntimeDialog as Object + + ' If the optional oLibContainer is not passed to the function then + ' DialogLibraries is loaded by default + If isMissing(oLibContainer ) then + oLibContainer = DialogLibraries + End If + + ' Loads the specified library, then loads the dialog + oLibContainer.loadLibrary(LibName) + oLib = oLibContainer.getByName(Libname) + oLibDialog = oLib.getByName(DialogName) + oRuntimeDialog = createUnoDialog(oLibDialog) + + ' Returns the runtime dialog object + loadDialog() = oRuntimeDialog +End Function + + + +' *** +' Gets the RGB integer values and new text string from the dialog +' then writes the new coloured text to the start and end of the document +' +' *** +Sub getFromDialog + Dim oDocument As Object + Dim oText As Object + Dim oCursor As Object + + ' Create a document object for the current document then create text and + ' cursor objects + oDocument = StarDesktop.ActiveFrame.Controller.Model + oText = oDocument.Text + oCursor = oText.createTextCursor() + + ' Write the coloured text to the start and end of the document + oCursor.gotoStart(false) + oCursor.CharColor = getColor() + oCursor.setString("New text at start: " + getNewText()) + oCursor.gotoEnd(false) + oCursor.CharColor = getColor() + oCursor.setString("New text at end: " + getNewText()) +End Sub + + + +' *** +' Reads the RGB integer values from the dialog +' +' returns long representing the RGB value +' *** +Function getColor() as Long + Dim oRedText as Object + Dim oGreenText as Object + Dim oBlueText as Object + Dim nColor As Long + + ' Get the three RGB values + oRedText = oDialog.GetControl("RedTextBox") + oGreenText = oDialog.GetControl("GreenTextBox") + oBlueText = oDialog.GetControl("BlueTextBox") + + ' Convert the values to long type and return the value + nColor = RGB(oRedText.Text,oGreenText.Text,oBlueText.Text) + getColor = nColor +End Function + + + +' *** +' Reads the new text from the dialog +' +' returns string the new text +' *** +Function getNewText() as String + Dim oNewText As Object + Dim sNewText As String + + ' Gets the string from dialog and returns the new text + oNewText = oDialog.GetControl("NewTextBox") + sNewText = oNewText.Text + getNewText = sNewText +End Function</script:module>
\ No newline at end of file diff --git a/scripting/examples/basic/InsertColouredTextDialog.xdl b/scripting/examples/basic/InsertColouredTextDialog.xdl new file mode 100644 index 000000000..2fa7768bc --- /dev/null +++ b/scripting/examples/basic/InsertColouredTextDialog.xdl @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd"> +<!-- + * 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 . +--> +<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="InsertColouredTextDialog" dlg:left="113" dlg:top="112" dlg:width="178" dlg:height="138" dlg:closeable="true" dlg:moveable="true"> + <dlg:bulletinboard> + <dlg:textfield dlg:id="RedTextBox" dlg:tab-index="0" dlg:left="37" dlg:top="26" dlg:width="20" dlg:height="17"/> + <dlg:textfield dlg:id="GreenTextBox" dlg:tab-index="1" dlg:left="73" dlg:top="26" dlg:width="20" dlg:height="17"/> + <dlg:textfield dlg:id="BlueTextBox" dlg:tab-index="2" dlg:left="109" dlg:top="26" dlg:width="20" dlg:height="17"/> + <dlg:textfield dlg:id="NewTextBox" dlg:tab-index="3" dlg:left="57" dlg:top="67" dlg:width="57" dlg:height="15"/> + <dlg:button dlg:id="CommandButton1" dlg:tab-index="4" dlg:left="49" dlg:top="97" dlg:width="75" dlg:height="15" dlg:value="Insert New Coloured Text"> + <script:event script:event-name="on-mousedown" script:location="application" script:macro-name="Standard.InsertColouredText.getFromDialog" script:language="StarBasic"/> + </dlg:button> + <dlg:text dlg:id="Label1" dlg:tab-index="5" dlg:left="44" dlg:top="12" dlg:width="7" dlg:height="10" dlg:value="R"/> + <dlg:text dlg:id="Label2" dlg:tab-index="6" dlg:left="78" dlg:top="12" dlg:width="7" dlg:height="10" dlg:value="G"/> + <dlg:text dlg:id="Label3" dlg:tab-index="7" dlg:left="114" dlg:top="12" dlg:width="7" dlg:height="10" dlg:value="B"/> + <dlg:text dlg:id="Label4" dlg:tab-index="8" dlg:left="71" dlg:top="56" dlg:width="26" dlg:height="8" dlg:value="New Text"/> + </dlg:bulletinboard> +</dlg:window>
\ No newline at end of file diff --git a/scripting/examples/basic/SearchAndReplace.xba b/scripting/examples/basic/SearchAndReplace.xba new file mode 100644 index 000000000..24c61d8e9 --- /dev/null +++ b/scripting/examples/basic/SearchAndReplace.xba @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd"> +<!-- + * 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 . +--> +<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SearchAndReplace" script:language="StarBasic">' *** +' SearchAndReplace basic script +' Uses a user interface to search and replace the specified strings +' +' author Neil Montgomery +' created August 12, 2002 +' *** + + +' Main subprocedure to start script +Sub Main + dialogShow() +End Sub + + +' Global reference to the dialog object +Dim oDialog as Object + + +' Uses the loadDialog subprocedure to load and execute the dialog box +Sub dialogShow + oDialog = loadDialog("Standard","SearchAndReplaceDialog") + oDialog.execute() +End Sub + + + +' *** +' Loads the dialog from the dialog library +' +' param Libname the library name where dialog is stored +' param DialogName the name of the dialog +' param oLibContainer library container to hold the loaded dialog library (optional) +' return runtime dialog object +' *** +Function loadDialog(Libname as String, DialogName as String, Optional oLibContainer) + Dim oLib as Object + Dim oLibDialog as Object + Dim oRuntimeDialog as Object + + If isMissing(oLibContainer ) then + oLibContainer = DialogLibraries + End If + oLibContainer.loadLibrary(LibName) + oLib = oLibContainer.getByName(Libname) + oLibDialog = oLib.getByName(DialogName) + oRuntimeDialog = createUnoDialog(oLibDialog) + loadDialog() = oRuntimeDialog +End Function + + + +' *** +' Creates a connection to the current document. +' Gets the search and replace keys from the dialog and replaces all +' instances of the search key with the replace key. +' +' *** +Sub getInfoFromDialog + Dim oDocument As Object + Dim oSearch As Object + Dim oFound As Object + Dim oFoundCursor As Object + Dim oSearchText as Object + Dim oReplaceText as Object + + ' Create a document object for the current document then create text and + ' cursor objects + oDocument = StarDesktop.ActiveFrame.Controller.Model + oSearch = oDocument.createSearchDescriptor + + ' Replace all instances of the search string with the replace string + oSearch.SearchString = getSearchKey() + oSearch.ReplaceString = getReplaceKey() + oDocument.replaceAll(oSearch) +End Sub + + +' *** +' Gets the search key string from the dialog +' +' returns string representing the search key +' *** +Function getSearchKey() as String + Dim sSearch As String + + ' Get the search key from the dialog + oSearchText = oDialog.GetControl("SearchKeyTextBox") + sSearch = oSearchText.Text + getSearchKey = sSearch +End Function + + + +' *** +' Gets the replace key string from the dialog +' +' returns string representing the replace key +' *** +Function getReplaceKey() as String + Dim sReplace As String + + ' Get the replace key from the dialog + oReplaceText = oDialog.GetControl("ReplaceKeyTextBox") + sReplace = oReplaceText.Text + getReplaceKey = sReplace +End Function</script:module> diff --git a/scripting/examples/basic/SearchAndReplaceDialog.xdl b/scripting/examples/basic/SearchAndReplaceDialog.xdl new file mode 100644 index 000000000..9503dbf57 --- /dev/null +++ b/scripting/examples/basic/SearchAndReplaceDialog.xdl @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd"> +<!-- + * 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 . +--> +<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="SearchAndReplaceDialog" dlg:left="113" dlg:top="112" dlg:width="178" dlg:height="138" dlg:closeable="true" dlg:moveable="true"> + <dlg:bulletinboard> + <dlg:textfield dlg:id="SearchKeyTextBox" dlg:tab-index="0" dlg:left="28" dlg:top="31" dlg:width="53" dlg:height="13"/> + <dlg:textfield dlg:id="ReplaceKeyTextBox" dlg:tab-index="1" dlg:left="102" dlg:top="31" dlg:width="51" dlg:height="13"/> + <dlg:button dlg:id="CommandButton1" dlg:tab-index="2" dlg:left="59" dlg:top="79" dlg:width="62" dlg:height="13" dlg:value="Search And Replace"> + <script:event script:event-name="on-mousedown" script:location="application" script:macro-name="Standard.SearchAndReplace.getInfoFromDialog" script:language="StarBasic"/> + </dlg:button> + <dlg:text dlg:id="Label1" dlg:tab-index="3" dlg:left="37" dlg:top="20" dlg:width="33" dlg:height="8" dlg:value="Search Key"/> + <dlg:text dlg:id="Label2" dlg:tab-index="4" dlg:left="109" dlg:top="20" dlg:width="35" dlg:height="8" dlg:value="Replace Key"/> + </dlg:bulletinboard> +</dlg:window>
\ No newline at end of file diff --git a/scripting/examples/basic/dialog.xlb b/scripting/examples/basic/dialog.xlb new file mode 100644 index 000000000..95dc3a123 --- /dev/null +++ b/scripting/examples/basic/dialog.xlb @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="Standard" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="InsertColouredTextDialog"/> + <library:element library:name="SearchAndReplaceDialog"/> +</library:library> diff --git a/scripting/examples/basic/script.xlb b/scripting/examples/basic/script.xlb new file mode 100644 index 000000000..fa7dd6103 --- /dev/null +++ b/scripting/examples/basic/script.xlb @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="Standard" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="InsertColouredText"/> + <library:element library:name="SearchAndReplace"/> +</library:library> diff --git a/scripting/examples/beanshell/Calc/CopyRange.bsh b/scripting/examples/beanshell/Calc/CopyRange.bsh new file mode 100644 index 000000000..d1e7a49f1 --- /dev/null +++ b/scripting/examples/beanshell/Calc/CopyRange.bsh @@ -0,0 +1,39 @@ +/* + * 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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.sheet.XSpreadsheet; +import com.sun.star.sheet.XSpreadsheets; +import com.sun.star.container.XIndexAccess; +import com.sun.star.sheet.XCellAddressable; +import com.sun.star.sheet.XCellRangeAddressable; +import com.sun.star.table.CellAddress; +import com.sun.star.table.CellRangeAddress; +import com.sun.star.sheet.XCellRangeMovement; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +XSpreadsheetDocument xDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class,oDoc); +XSpreadsheets xSheets = xDoc.getSheets(); +XIndexAccess xSheetsIA = UnoRuntime.queryInterface(XIndexAccess.class, xSheets); +XSpreadsheet xSheet = UnoRuntime.queryInterface(com.sun.star.sheet.XSpreadsheet.class, xSheetsIA.getByIndex(0)); + +XCellRangeAddressable xAddr1 = UnoRuntime.queryInterface(XCellRangeAddressable.class, xSheet.getCellRangeByName("A1:A10") ); +CellRangeAddress source = xAddr1.getRangeAddress(); + +XCellAddressable xAddr2 = UnoRuntime.queryInterface(XCellAddressable.class, xSheet.getCellRangeByName("B1").getCellByPosition( 0, 0 ) ); +CellAddress target = xAddr2.getCellAddress(); + +XCellRangeMovement xCRM = UnoRuntime.queryInterface(XCellRangeMovement.class, xSheet); +xCRM.copyRange(target, source); + +return 0; diff --git a/scripting/examples/beanshell/Calc/FixView.bsh b/scripting/examples/beanshell/Calc/FixView.bsh new file mode 100644 index 000000000..4ea2c62f9 --- /dev/null +++ b/scripting/examples/beanshell/Calc/FixView.bsh @@ -0,0 +1,34 @@ +/* + * 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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.sheet.XSpreadsheet; +import com.sun.star.sheet.XSpreadsheets; +import com.sun.star.container.XIndexAccess; +import com.sun.star.sheet.XViewFreezable; +import com.sun.star.sheet.XViewPane; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +XSpreadsheetDocument xDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class,oDoc); +XSpreadsheets xSheets = xDoc.getSheets(); +XIndexAccess xSheetsIA = UnoRuntime.queryInterface(XIndexAccess.class, xSheets); +XSpreadsheet xSheet = UnoRuntime.queryInterface(com.sun.star.sheet.XSpreadsheet.class, xSheetsIA.getByIndex(0)); + +XViewFreezable xFreeze = UnoRuntime.queryInterface(XViewFreezable.class, oDoc.getCurrentController() ); +xFreeze.freezeAtPosition(2, 3); + +XViewPane xViewPane = UnoRuntime.queryInterface(XViewPane.class, oDoc.getCurrentController() ); +xViewPane.setFirstVisibleColumn(12); +xViewPane.setFirstVisibleRow(149); + +return 0; diff --git a/scripting/examples/beanshell/Calc/InsertSheet.bsh b/scripting/examples/beanshell/Calc/InsertSheet.bsh new file mode 100644 index 000000000..ef68f9b3e --- /dev/null +++ b/scripting/examples/beanshell/Calc/InsertSheet.bsh @@ -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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.sheet.XSpreadsheets; +import com.sun.star.lang.XMultiServiceFactory; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +XSpreadsheetDocument xDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class,oDoc); +XSpreadsheets xSheets = xDoc.getSheets(); + +xSheets.insertNewByName("First new sheet", (short)0); +xSheets.insertNewByName("Second new sheet", (short)1); + +return 0; diff --git a/scripting/examples/beanshell/Calc/ProtectSheet.bsh b/scripting/examples/beanshell/Calc/ProtectSheet.bsh new file mode 100644 index 000000000..eea3bd511 --- /dev/null +++ b/scripting/examples/beanshell/Calc/ProtectSheet.bsh @@ -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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.sheet.XSpreadsheet; +import com.sun.star.sheet.XSpreadsheets; +import com.sun.star.container.XIndexAccess; +import com.sun.star.util.XProtectable; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +XSpreadsheetDocument xDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class, oDoc); +XSpreadsheets xSheets = xDoc.getSheets(); +XIndexAccess xSheetsIA = UnoRuntime.queryInterface(XIndexAccess.class, xSheets); +XSpreadsheet xSheet = UnoRuntime.queryInterface(XSpreadsheet.class, xSheetsIA.getByIndex(0)); +XProtectable xProtectable = UnoRuntime.queryInterface(XProtectable.class, xSheet); +xProtectable.protect("myPassword"); + +//xProtectable.unprotect("myPassword"); + +return 0; diff --git a/scripting/examples/beanshell/Calc/SelectCell.bsh b/scripting/examples/beanshell/Calc/SelectCell.bsh new file mode 100644 index 000000000..7d8f5a381 --- /dev/null +++ b/scripting/examples/beanshell/Calc/SelectCell.bsh @@ -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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.sheet.XSpreadsheet; +import com.sun.star.sheet.XSpreadsheets; +import com.sun.star.view.XSelectionSupplier; +import com.sun.star.container.XIndexAccess; +import com.sun.star.table.XCellRange; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +XSpreadsheetDocument xDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class,oDoc); +XSpreadsheets xSheets = xDoc.getSheets(); +XIndexAccess xSheetsIA = UnoRuntime.queryInterface(XIndexAccess.class, xSheets); +XSpreadsheet xSheet = UnoRuntime.queryInterface(com.sun.star.sheet.XSpreadsheet.class, xSheetsIA.getByIndex(0)); +XCellRange xResultRange = xSheet.getCellRangeByName("B20"); +XSelectionSupplier xSel = UnoRuntime.queryInterface(XSelectionSupplier.class, oDoc.getCurrentController()); +xSel.select(xResultRange); + +return 0; diff --git a/scripting/examples/beanshell/Calc/parcel-descriptor.xml b/scripting/examples/beanshell/Calc/parcel-descriptor.xml new file mode 100644 index 000000000..155d53c9b --- /dev/null +++ b/scripting/examples/beanshell/Calc/parcel-descriptor.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><parcel xmlns:parcel="scripting.dtd" language="BeanShell"> +<script language="BeanShell"><locale lang="en"><displayname value="SelectCell.bsh"/><description>SelectCell.bsh</description></locale><logicalname value="SelectCell.bsh"/><functionname value="SelectCell.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="InsertSheet.bsh"/><description>InsertSheet.bsh</description></locale><logicalname value="InsertSheet.bsh"/><functionname value="InsertSheet.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="ProtectSheet.bsh"/><description>ProtectSheet.bsh</description></locale><logicalname value="ProtectSheet.bsh"/><functionname value="ProtectSheet.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="CopyRange.bsh"/><description>CopyRange.bsh</description></locale><logicalname value="CopyRange.bsh"/><functionname value="CopyRange.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="FixView.bsh"/><description>FixView.bsh</description></locale><logicalname value="FixView.bsh"/><functionname value="FixView.bsh"/></script></parcel>
\ No newline at end of file diff --git a/scripting/examples/beanshell/Capitalise/capitalise.bsh b/scripting/examples/beanshell/Capitalise/capitalise.bsh new file mode 100644 index 000000000..6b68e010f --- /dev/null +++ b/scripting/examples/beanshell/Capitalise/capitalise.bsh @@ -0,0 +1,111 @@ +/* + * 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 . + */ +// Change the case of a selection, or current word from upper case, +// to first char upper case, to all lower case to upper case... +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.view.XSelectionSupplier; +import com.sun.star.container.XIndexAccess; +import com.sun.star.text.XText; +import com.sun.star.text.XTextRange; +import com.sun.star.text.XWordCursor; +import com.sun.star.script.provider.XScriptContext; + +// return the new string based on the string passed in +String getNewString( theString ) { + String newString; + if(theString==null || theString.length()==0) { + return newString; + } + // should we tokenize on "."? + if(Character.isUpperCase(theString.charAt(0)) && theString.length()>=2 && Character.isUpperCase(theString.charAt(1))) { // first two chars are UC => first UC, rest LC + newString=theString.substring(0,1).toUpperCase()+theString.substring(1).toLowerCase(); + } else if (Character.isUpperCase(theString.charAt(0))) { // first char UC => all to LC + newString=theString.toLowerCase(); + } else { // all to UC. + newString=theString.toUpperCase(); + } + return newString; +} + +//the method that does the work +void capitalise() { + + // get the number of regions selected + count = xIndexAccess.getCount(); + if(count>=1) { //ie we have a selection + for(i=0;i<count;i++) { + // get the i-th region selected + xTextRange = (XTextRange) + UnoRuntime.queryInterface(XTextRange.class, xIndexAccess.getByIndex(i)); + System.out.println("string: "+xTextRange.getString()); + // get the selected string + theString = xTextRange.getString(); + if(theString.length()==0) { + // sadly we can have a selection where nothing is selected + // in this case we get the XWordCursor and make a selection! + xText = (XText) + UnoRuntime.queryInterface(XText.class, xTextRange.getText()); + xWordCursor = (XWordCursor) + UnoRuntime.queryInterface(XWordCursor.class, xText.createTextCursorByRange(xTextRange)); + // move the Word cursor to the start of the word if it's not + // already there + if(!xWordCursor.isStartOfWord()) { + xWordCursor.gotoStartOfWord(false); + } + // move the cursor to the next word, selecting all chars + // in between + xWordCursor.gotoNextWord(true); + // get the selected string + theString = xWordCursor.getString(); + // get the new string + newString = getNewString(theString); + if(newString!=null) { + // set the new string + xWordCursor.setString(newString); + // keep the current selection + xSelectionSupplier.select(xWordCursor); + } + } else { + newString = getNewString( theString ); + if(newString!=null) { + // set the new string + xTextRange.setString(newString); + // keep the current selection + xSelectionSupplier.select(xTextRange); + } + } + + } + } +} + +// The XSCRIPTCONTEXT variable is of type XScriptContext and is available to +// all BeanShell scripts executed by the Script Framework +xModel = (XModel) + UnoRuntime.queryInterface(XModel.class, XSCRIPTCONTEXT.getDocument()); +//the writer controller impl supports the css.view.XSelectionSupplier interface +xSelectionSupplier = (XSelectionSupplier) + UnoRuntime.queryInterface(XSelectionSupplier.class, xModel.getCurrentController()); +//see section 7.5.1 of developers' guide +xIndexAccess = (XIndexAccess) + UnoRuntime.queryInterface(XIndexAccess.class, xSelectionSupplier.getSelection()); + +//call the method that does the work +capitalise(); +return 0; diff --git a/scripting/examples/beanshell/Capitalise/parcel-descriptor.xml b/scripting/examples/beanshell/Capitalise/parcel-descriptor.xml new file mode 100644 index 000000000..d6fb419aa --- /dev/null +++ b/scripting/examples/beanshell/Capitalise/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="BeanShell" xmlns:parcel="scripting.dtd"> + + <script language="BeanShell"> + <locale lang="en"> + <displayname value="Capitalise"/> + <description> + Change the case of a selection, or current word from upper case, to first char upper case, to all lower case to upper case... + </description> + </locale> + <functionname value="capitalise.bsh"/> + <logicalname value="Capitalise.BeanShell"/> + </script> + +</parcel> diff --git a/scripting/examples/beanshell/HelloWorld/helloworld.bsh b/scripting/examples/beanshell/HelloWorld/helloworld.bsh new file mode 100644 index 000000000..4ff9cd42e --- /dev/null +++ b/scripting/examples/beanshell/HelloWorld/helloworld.bsh @@ -0,0 +1,34 @@ +/* + * 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 . + */ +// Hello World in BeanShell +import com.sun.star.uno.UnoRuntime; +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; +import com.sun.star.text.XTextRange; + +// get the document from the scripting context which is made available to all +// scripts +oDoc = XSCRIPTCONTEXT.getDocument(); +//get the XTextDocument interface +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +//get the XText interface +xText = xTextDoc.getText(); +// get an (empty) XTextRange at the end of the document +xTextRange = xText.getEnd(); +// set the string +xTextRange.setString( "Hello World (in BeanShell)" ); diff --git a/scripting/examples/beanshell/HelloWorld/parcel-descriptor.xml b/scripting/examples/beanshell/HelloWorld/parcel-descriptor.xml new file mode 100644 index 000000000..e4c073b5f --- /dev/null +++ b/scripting/examples/beanshell/HelloWorld/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="BeanShell" xmlns:parcel="scripting.dtd"> + + <script language="BeanShell"> + <locale lang="en"> + <displayname value="Hello World"/> + <description> + Adds the string "Hello World" into the current text doc. + </description> + </locale> + <functionname value="helloworld.bsh"/> + <logicalname value="HelloWorld.BeanShell"/> + </script> + +</parcel> diff --git a/scripting/examples/beanshell/Highlight/ButtonPressHandler.bsh b/scripting/examples/beanshell/Highlight/ButtonPressHandler.bsh new file mode 100644 index 000000000..ac6efacce --- /dev/null +++ b/scripting/examples/beanshell/Highlight/ButtonPressHandler.bsh @@ -0,0 +1,123 @@ +/* + * 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 . + */ +// this code is bound to the events generated by the buttons in the dialog +// it will close the dialog or find and highlight the text entered in the +// dialog (depending on the button pressed) +import com.sun.star.uno.*; +import com.sun.star.awt.*; +import com.sun.star.lang.*; +import com.sun.star.beans.*; +import com.sun.star.util.*; +import com.sun.star.script.framework.browse.DialogFactory; + +// Get the ActionEvent object from the ARGUMENTS list +ActionEvent event = (ActionEvent) ARGUMENTS[0]; + +// Each argument is of type Any so we must use the AnyConverter class to +// convert it into the interface or primitive type we expect +XButton button = (XButton)AnyConverter.toObject( + new Type(XButton.class), event.Source); + +// We can now query for the model of the button and get its properties +XControl control = (XControl)UnoRuntime.queryInterface(XControl.class, button); +XControlModel cmodel = control.getModel(); +XPropertySet pset = (XPropertySet)UnoRuntime.queryInterface( + XPropertySet.class, cmodel); + +if (pset.getPropertyValue("Label").equals("Exit")) +{ + // We can get the XDialog in which this control appears by calling + // getContext() on the XControl interface + XDialog xDialog = (XDialog)UnoRuntime.queryInterface( + XDialog.class, control.getContext()); + + // Close the dialog + xDialog.endExecute(); +} +else +{ + // We can get the list of controls for this dialog by calling + // getContext() on the XControl interface of the button + XControlContainer controls = (XControlContainer)UnoRuntime.queryInterface( + XControlContainer.class, control.getContext()); + + // Now get the text field control from the list + XTextComponent textField = (XTextComponent) + UnoRuntime.queryInterface( + XTextComponent.class, controls.getControl("HighlightTextField")); + + String searchKey = textField.getText(); + + // highlight the text in red + java.awt.Color cRed = new java.awt.Color(255, 0, 0); + int red = cRed.getRGB(); + + XReplaceable replaceable = (XReplaceable) + UnoRuntime.queryInterface(XReplaceable.class, XSCRIPTCONTEXT.getDocument()); + + XReplaceDescriptor descriptor = + (XReplaceDescriptor) replaceable.createReplaceDescriptor(); + + // Gets a XPropertyReplace object for altering the properties + // of the replaced text + XPropertyReplace xPropertyReplace = (XPropertyReplace) + UnoRuntime.queryInterface(XPropertyReplace.class, descriptor); + + // Sets the replaced text property fontweight value to Bold + PropertyValue wv = new PropertyValue("CharWeight", -1, + new Float(com.sun.star.awt.FontWeight.BOLD), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Sets the replaced text property color value to RGB parameter + PropertyValue cv = new PropertyValue("CharColor", -1, + new Integer(red), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Apply the properties + PropertyValue[] props = new PropertyValue[] { cv, wv }; + + try { + xPropertyReplace.setReplaceAttributes(props); + + // Only matches whole words and case sensitive + descriptor.setPropertyValue( + "SearchCaseSensitive", new Boolean(true)); + descriptor.setPropertyValue("SearchWords", new Boolean(true)); + } + catch (com.sun.star.beans.UnknownPropertyException upe) { + System.err.println("Error setting up search properties"); + return; + } + catch (com.sun.star.beans.PropertyVetoException pve) { + System.err.println("Error setting up search properties"); + return; + } + catch (com.sun.star.lang.WrappedTargetException wte) { + System.err.println("Error setting up search properties"); + return; + } + + // Replaces all instances of searchKey with new Text properties + // and gets the number of instances of the searchKey + descriptor.setSearchString(searchKey); + descriptor.setReplaceString(searchKey); + replaceable.replaceAll(descriptor); +} + +// BeanShell scripts in LibreOffice should always return 0 +return 0; diff --git a/scripting/examples/beanshell/Highlight/ShowDialog.bsh b/scripting/examples/beanshell/Highlight/ShowDialog.bsh new file mode 100644 index 000000000..95ccc32bb --- /dev/null +++ b/scripting/examples/beanshell/Highlight/ShowDialog.bsh @@ -0,0 +1,140 @@ +/* + * 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 . + */ +// this script serves as an example of how to launch a Basic Dialog +// from a script +import com.sun.star.uno.UnoRuntime; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.Type; +import com.sun.star.uno.AnyConverter; +import com.sun.star.text.XTextDocument; +import com.sun.star.beans.PropertyValue; +import com.sun.star.script.XLibraryContainer; +import com.sun.star.awt.*; +import com.sun.star.util.*; + +boolean tryLoadingLibrary( xmcf, context, name ) +{ + try + { + obj = xmcf.createInstanceWithContext( + "com.sun.star.script.Application" + name + "LibraryContainer", + context.getComponentContext()); + + xLibraryContainer = (XLibraryContainer) + UnoRuntime.queryInterface(XLibraryContainer.class, obj); + + System.err.println("Got XLibraryContainer"); + + serviceObj = context.getComponentContext().getValueByName( + "/singletons/com.sun.star.util.theMacroExpander"); + + xme = (XMacroExpander) AnyConverter.toObject( + new Type(XMacroExpander.class), serviceObj); + + bootstrapName = "bootstraprc"; + if (System.getProperty("os.name").startsWith("Windows")) + { + bootstrapName = "bootstrap.ini"; + } + + libURL = xme.expandMacros( + "$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/basic/ScriptBindingLibrary/" + + name.toLowerCase() + ".xlb/"); + + System.err.println("libURL is: " + libURL); + + xLibraryContainer.createLibraryLink( + "ScriptBindingLibrary", libURL, false); + + System.err.println("liblink created"); + + } + catch (com.sun.star.uno.Exception e) + { + System.err.println("Got an exception loading lib: " + e.getMessage()); + return false; + } + return true; +} + +// get the XMultiComponentFactory from the XSCRIPTCONTEXT +XMultiComponentFactory xmcf = + XSCRIPTCONTEXT.getComponentContext().getServiceManager(); + +Object[] args = new Object[1]; +args[0] = XSCRIPTCONTEXT.getDocument(); + +Object obj; +try { + // try to create an instance of the DialogProvider + obj = xmcf.createInstanceWithArgumentsAndContext( + "com.sun.star.awt.DialogProvider", args, + XSCRIPTCONTEXT.getComponentContext()); + /* + obj = xmcf.createInstanceWithContext( + "com.sun.star.awt.DialogProvider", + XSCRIPTCONTEXT.getComponentContext()); + */ +} +catch (com.sun.star.uno.Exception e) { + System.err.println("Error getting DialogProvider object"); + return 0; +} + +// get the XDialogProvider interface from the object created above +XDialogProvider xDialogProvider = (XDialogProvider) + UnoRuntime.queryInterface(XDialogProvider.class, obj); + +System.err.println("Got DialogProvider, now get dialog"); + +try { + // try to create the Highlight dialog (found in the ScriptBindingLibrary) + findDialog = xDialogProvider.createDialog("vnd.sun.star.script:" + + "ScriptBindingLibrary.Highlight?location=application"); + if( findDialog == null ) + { + if (tryLoadingLibrary(xmcf, XSCRIPTCONTEXT, "Dialog") == false || + tryLoadingLibrary(xmcf, XSCRIPTCONTEXT, "Script") == false) + { + System.err.println("Error loading ScriptBindingLibrary"); + return 0; + } + else + { + // try to create the Highlight dialog (found in the ScriptBindingLibrary) + findDialog = xDialogProvider.createDialog("vnd.sun.star.script:" + + "ScriptBindingLibrary.Highlight?location=application"); + } + } +} +catch (java.lang.Exception e) { + System.err.println("Got exception on first creating dialog: " + + e.getMessage()); +} + +// execute the dialog in a new thread (so that this script can finish) +Thread t = new Thread() { + public void run() { + findDialog.execute(); + } +}; +t.start(); + +return 0; diff --git a/scripting/examples/beanshell/Highlight/highlighter.bsh b/scripting/examples/beanshell/Highlight/highlighter.bsh new file mode 100644 index 000000000..a69f76e1a --- /dev/null +++ b/scripting/examples/beanshell/Highlight/highlighter.bsh @@ -0,0 +1,166 @@ +/* + * 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.uno.UnoRuntime; +import com.sun.star.util.XReplaceable; +import com.sun.star.util.XReplaceDescriptor; +import com.sun.star.util.XPropertyReplace; +import com.sun.star.beans.PropertyValue; +import com.sun.star.text.XTextDocument; +import com.sun.star.script.provider.XScriptContext; + +int replaceText(searchKey, color, bold) { + + result = 0; + + try { + // Create an XReplaceable object and an XReplaceDescriptor + replaceable = (XReplaceable) + UnoRuntime.queryInterface(XReplaceable.class, xTextDocument); + + descriptor = + (XReplaceDescriptor) replaceable.createReplaceDescriptor(); + + // Gets a XPropertyReplace object for altering the properties + // of the replaced text + xPropertyReplace = (XPropertyReplace) + UnoRuntime.queryInterface(XPropertyReplace.class, descriptor); + + // Sets the replaced text property fontweight value to Bold or Normal + wv = null; + if (bold) { + wv = new PropertyValue("CharWeight", -1, + new Float(com.sun.star.awt.FontWeight.BOLD), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + } + else { + wv = new PropertyValue("CharWeight", -1, + new Float(com.sun.star.awt.FontWeight.NORMAL), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + } + + // Sets the replaced text property color value to RGB color parameter + cv = new PropertyValue("CharColor", -1, new Integer(color), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Apply the properties + PropertyValue[] props = { cv, wv }; + xPropertyReplace.setReplaceAttributes(props); + + // Only matches whole words and case sensitive + descriptor.setPropertyValue("SearchCaseSensitive", new Boolean(true)); + descriptor.setPropertyValue("SearchWords", new Boolean(true)); + + // Replaces all instances of searchKey with new Text properties + // and gets the number of instances of the searchKey + descriptor.setSearchString(searchKey); + descriptor.setReplaceString(searchKey); + result = replaceable.replaceAll(descriptor); + + } + catch (Exception e) { + } + + return result; +} + +searchKey = ""; + +// The XSCRIPTCONTEXT variable is of type XScriptContext and is available to +// all BeanShell scripts executed by the Script Framework +xTextDocument = (XTextDocument) + UnoRuntime.queryInterface(XTextDocument.class, XSCRIPTCONTEXT.getDocument()); + +// Create a JButton and add an ActionListener +// When clicked the value for the searchKey is read and passed to replaceText +myListener = new ActionListener() { + actionPerformed(ActionEvent e) { + searchKey = findTextBox.getText(); + + if(searchKey.equalsIgnoreCase("")) { + JOptionPane.showMessageDialog(null, + "No text entered for search", + "No text", JOptionPane.INFORMATION_MESSAGE); + } + else { + // highlight the text in red + cRed = new Color(255, 0, 0); + red = cRed.getRGB(); + num = replaceText(searchKey, red, true); + + if(num > 0) { + int response = JOptionPane.showConfirmDialog(null, + searchKey + " was found " + num + + " times\nDo you wish to keep the text highlighted?", + "Confirm highlight", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + + if (response == 1) { + cBlack = new Color(255, 255, 255); + black = cBlack.getRGB(); + replaceText(searchKey, black, false); + } + } + else { + JOptionPane.showMessageDialog(null, + "No matches were found", "Not found", + JOptionPane.INFORMATION_MESSAGE); + } + } + } +}; + + +exitListener = new ActionListener() { + actionPerformed(ActionEvent e) { + frame.dispose(); + } +}; + + +searchButton = new JButton("Highlight"); +searchButton.addActionListener(myListener); + +exitButton = new JButton("Exit"); +exitButton.addActionListener(exitListener); + +buttonPanel = new JPanel(); +buttonPanel.setLayout(new FlowLayout()); +buttonPanel.add(searchButton); +buttonPanel.add(exitButton); + + +// Create a JPanel containing one JTextField for the search text. +searchPanel = new JPanel(); +searchPanel.setLayout(new FlowLayout()); +findTextBox = new JTextField(20); +findWhat = new JLabel("Find What: "); +searchPanel.add(findWhat); +searchPanel.add(findTextBox); + +// Create frame and add a window listener +frame = new JFrame("Highlight Text"); +frame.setSize(350,130); +frame.setLocation(430,430); +frame.setResizable(false); +// Add the panel and button to the frame +frame.getContentPane().setLayout(new GridLayout(2,1,10,10)); +frame.getContentPane().add(searchPanel); +frame.getContentPane().add(buttonPanel); + +frame.setVisible(true); +frame.pack(); diff --git a/scripting/examples/beanshell/Highlight/parcel-descriptor.xml b/scripting/examples/beanshell/Highlight/parcel-descriptor.xml new file mode 100644 index 000000000..a2a3773a9 --- /dev/null +++ b/scripting/examples/beanshell/Highlight/parcel-descriptor.xml @@ -0,0 +1,41 @@ +<?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 . +--> +<parcel language="BeanShell" xmlns:parcel="scripting.dtd"> + <script language="BeanShell"> + <locale lang="en"> + <displayname value="ShowDialog" /> + <description> + Example of how to show a dialog from BeanShell + </description> + </locale> + <functionname value="ShowDialog.bsh" /> + <logicalname value="ShowDialog.BeanShell" /> + </script> + <script language="BeanShell"> + <locale lang="en"> + <displayname value="ButtonPressHandler" /> + <description> + Example of handle button press events for the Dialog + </description> + </locale> + <functionname value="ButtonPressHandler.bsh" /> + <logicalname value="ButtonPressHandler.BeanShell" /> + </script> +</parcel> + diff --git a/scripting/examples/beanshell/InteractiveBeanShell/interactive.bsh b/scripting/examples/beanshell/InteractiveBeanShell/interactive.bsh new file mode 100644 index 000000000..0e82f1df0 --- /dev/null +++ b/scripting/examples/beanshell/InteractiveBeanShell/interactive.bsh @@ -0,0 +1,21 @@ +/* + * 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 . + */ +// Pops up a window into which you can type BeanShell code and run it +// against the current document +editor(); +return 0; diff --git a/scripting/examples/beanshell/InteractiveBeanShell/parcel-descriptor.xml b/scripting/examples/beanshell/InteractiveBeanShell/parcel-descriptor.xml new file mode 100644 index 000000000..0911ac56a --- /dev/null +++ b/scripting/examples/beanshell/InteractiveBeanShell/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="BeanShell" xmlns:parcel="scripting.dtd"> + + <script language="BeanShell"> + <locale lang="en"> + <displayname value="Interactive BeanShell"/> + <description> + Pops up a window into which you can type BeanShell code and run it against the current document + </description> + </locale> + <functionname value="interactive.bsh"/> + <logicalname value="Interactive.BeanShell"/> + </script> + +</parcel> diff --git a/scripting/examples/beanshell/MemoryUsage/memusage.bsh b/scripting/examples/beanshell/MemoryUsage/memusage.bsh new file mode 100644 index 000000000..3929f6435 --- /dev/null +++ b/scripting/examples/beanshell/MemoryUsage/memusage.bsh @@ -0,0 +1,137 @@ +/* + * 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.uno.UnoRuntime; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.document.XEmbeddedObjectSupplier; +import com.sun.star.awt.ActionEvent; +import com.sun.star.awt.Rectangle; +import com.sun.star.beans.XPropertySet; +import com.sun.star.beans.PropertyValue; + +import com.sun.star.container.*; +import com.sun.star.chart.*; +import com.sun.star.table.*; +import com.sun.star.sheet.*; + +import com.sun.star.script.provider.XScriptContext; + +createSpreadsheet() +{ + loader = (XComponentLoader) + UnoRuntime.queryInterface( + XComponentLoader.class, XSCRIPTCONTEXT.getDesktop()); + + comp = loader.loadComponentFromURL( + "private:factory/scalc", "_blank", 4, new PropertyValue[0]); + + doc = (XSpreadsheetDocument) + UnoRuntime.queryInterface(XSpreadsheetDocument.class, comp); + + index = (XIndexAccess) + UnoRuntime.queryInterface(XIndexAccess.class, doc.getSheets()); + + sheet = (XSpreadsheet) AnyConverter.toObject( + new Type(com.sun.star.sheet.XSpreadsheet.class), index.getByIndex(0)); + + return sheet; +} + +addData(sheet, date, total, free) +{ + // set the labels + sheet.getCellByPosition(0, 0).setFormula("Used"); + sheet.getCellByPosition(0, 1).setFormula("Free"); + sheet.getCellByPosition(0, 2).setFormula("Total"); + + // set the values in the cells + sheet.getCellByPosition(1, 0).setValue(total - free); + sheet.getCellByPosition(1, 1).setValue(free); + sheet.getCellByPosition(1, 2).setValue(total); +} + +addChart(sheet) +{ + rect = new Rectangle(); + rect.X = 500; + rect.Y = 3000; + rect.Width = 10000; + rect.Height = 8000; + + range = (XCellRange) UnoRuntime.queryInterface(XCellRange.class, sheet); + myRange = range.getCellRangeByName("A1:B2"); + + rangeAddr = (XCellRangeAddressable) + UnoRuntime.queryInterface(XCellRangeAddressable.class, myRange); + + myAddr = rangeAddr.getRangeAddress(); + + CellRangeAddress[] addr = new CellRangeAddress[1]; + addr[0] = myAddr; + + supp = (XTableChartsSupplier) + UnoRuntime.queryInterface( XTableChartsSupplier.class, sheet); + charts = supp.getCharts(); + charts.addNewByName("Example", rect, addr, false, true); + + try { Thread.sleep(3000); } catch (java.lang.InterruptedException e) { } + + // get the diagram and Change some of the properties + chartsAccess = (XNameAccess) + UnoRuntime.queryInterface( XNameAccess.class, charts); + + tchart = (XTableChart) + UnoRuntime.queryInterface( + XTableChart.class, chartsAccess.getByName("Example")); + + eos = (XEmbeddedObjectSupplier) + UnoRuntime.queryInterface( XEmbeddedObjectSupplier.class, tchart ); + xifc = eos.getEmbeddedObject(); + + xChart = (XChartDocument) + UnoRuntime.queryInterface(XChartDocument.class, xifc); + + xDocMSF = (XMultiServiceFactory) + UnoRuntime.queryInterface(XMultiServiceFactory.class, xChart); + + diagObject = xDocMSF.createInstance("com.sun.star.chart.PieDiagram"); + xDiagram = (XDiagram) + UnoRuntime.queryInterface(XDiagram.class, diagObject); + xChart.setDiagram(xDiagram); + + propset = (XPropertySet) + UnoRuntime.queryInterface( XPropertySet.class, xChart.getTitle() ); + propset.setPropertyValue("String", "JVM Memory Usage"); +} + +runtime = Runtime.getRuntime(); +generator = new Random(); +date = new Date(); + +// allocate a random number of bytes so that the data changes +len = (int)(generator.nextFloat() * runtime.freeMemory() / 5); +bytes = new byte[len]; + +sheet = createSpreadsheet(); +addData(sheet, date.toString(), runtime.totalMemory(), runtime.freeMemory()); +addChart(sheet); + +return 0; diff --git a/scripting/examples/beanshell/MemoryUsage/parcel-descriptor.xml b/scripting/examples/beanshell/MemoryUsage/parcel-descriptor.xml new file mode 100644 index 000000000..46c7ce422 --- /dev/null +++ b/scripting/examples/beanshell/MemoryUsage/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="BeanShell" xmlns:parcel="scripting.dtd"> + + <script language="BeanShell"> + <locale lang="en"> + <displayname value="BeanShell JVM Usage"/> + <description> + Updates a spreadsheet with the current memory usage statistics for the Java Virtual Machine + </description> + </locale> + <functionname value="memusage.bsh"/> + <logicalname value="MemoryUsage.BeanShell"/> + </script> + +</parcel> diff --git a/scripting/examples/beanshell/WordCount/parcel-descriptor.xml b/scripting/examples/beanshell/WordCount/parcel-descriptor.xml new file mode 100644 index 000000000..5887e4991 --- /dev/null +++ b/scripting/examples/beanshell/WordCount/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="BeanShell" xmlns:parcel="scripting.dtd"> + + <script language="BeanShell"> + <locale lang="en"> + <displayname value="Word Count"/> + <description> + Provides a word count of the selected text in A Writer document. + </description> + </locale> + <functionname value="wordcount.bsh"/> + <logicalname value="WordCount.BeanShell"/> + </script> + +</parcel> diff --git a/scripting/examples/beanshell/WordCount/wordcount.bsh b/scripting/examples/beanshell/WordCount/wordcount.bsh new file mode 100644 index 000000000..b068d8a7d --- /dev/null +++ b/scripting/examples/beanshell/WordCount/wordcount.bsh @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +//Provides a word count of the selected text in a Writer document. +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.view.XSelectionSupplier; +import com.sun.star.container.XIndexAccess; +import com.sun.star.text.XText; +import com.sun.star.text.XTextRange; +import com.sun.star.script.provider.XScriptContext; + +// display the count in a Swing dialog +void doDisplay(numWords) { + wordsLabel = new JLabel("Word count = " + numWords); + closeButton = new JButton("Close"); + frame = new JFrame("Word Count"); + closeButton.addActionListener(new ActionListener() { + actionPerformed(ActionEvent e) { + frame.setVisible(false); + } + }); + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(wordsLabel, BorderLayout.CENTER); + frame.getContentPane().add(closeButton, BorderLayout.SOUTH); + frame.pack(); + frame.setSize(190,90); + frame.setLocation(430,430); + frame.setVisible(true); +} + +int wordcount() { + + result = 0; + + // iterate through each of the selections + count = xIndexAccess.getCount(); + for(i=0;i<count;i++) { + // get the XTextRange of the selection + xTextRange = (XTextRange) + UnoRuntime.queryInterface(XTextRange.class, xIndexAccess.getByIndex(i)); + //System.out.println("string: "+xTextRange.getString()); + // use the standard J2SE delimiters to tokenize the string + // obtained from the XTextRange + strTok = new StringTokenizer(xTextRange.getString()); + result += strTok.countTokens(); + } + + doDisplay(result); + return result; +} + +// The XSCRIPTCONTEXT variable is of type XScriptContext and is available to +// all BeanShell scripts executed by the Script Framework +xModel = (XModel) + UnoRuntime.queryInterface(XModel.class, XSCRIPTCONTEXT.getDocument()); +//the writer controller impl supports the css.view.XSelectionSupplier interface +xSelectionSupplier = (XSelectionSupplier) + UnoRuntime.queryInterface(XSelectionSupplier.class, xModel.getCurrentController()); +//see section 7.5.1 of developers' guide +// the getSelection provides an XIndexAccess to the one or more selections +xIndexAccess = (XIndexAccess) + UnoRuntime.queryInterface(XIndexAccess.class, xSelectionSupplier.getSelection()); + +count = wordcount(); +System.out.println("count = "+count); +return 0; diff --git a/scripting/examples/beanshell/Writer/ChangeFont.bsh b/scripting/examples/beanshell/Writer/ChangeFont.bsh new file mode 100644 index 000000000..7cbea8fc3 --- /dev/null +++ b/scripting/examples/beanshell/Writer/ChangeFont.bsh @@ -0,0 +1,36 @@ +/* + * 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/. + */ + +import com.sun.star.beans.XPropertySet; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.container.XEnumeration; +import com.sun.star.container.XEnumerationAccess; + +import com.sun.star.text.XTextDocument; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xText = xTextDoc.getText(); +XEnumerationAccess xEnumAcc = (XEnumerationAccess)(UnoRuntime.queryInterface(XEnumerationAccess.class, xText)); +XEnumeration xEnum = xEnumAcc.createEnumeration(); +while (xEnum.hasMoreElements()) { + Object xObj = xEnum.nextElement(); + XServiceInfo xServiceInfo = (XServiceInfo) UnoRuntime.queryInterface(XServiceInfo.class, xObj); + if (xServiceInfo.supportsService("com.sun.star.text.Paragraph")) { + XPropertySet xSet = UnoRuntime.queryInterface(XPropertySet.class, xServiceInfo ); + xSet.setPropertyValue( "CharHeight", 28 ); + xSet.setPropertyValue( "CharFontName", "Liberation Sans" ); + } +} + +return 0; diff --git a/scripting/examples/beanshell/Writer/ChangeParaAdjust.bsh b/scripting/examples/beanshell/Writer/ChangeParaAdjust.bsh new file mode 100644 index 000000000..db5e5b66a --- /dev/null +++ b/scripting/examples/beanshell/Writer/ChangeParaAdjust.bsh @@ -0,0 +1,37 @@ +/* + * 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/. + */ + +import com.sun.star.beans.XPropertySet; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.container.XEnumeration; +import com.sun.star.container.XEnumerationAccess; + +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xText = xTextDoc.getText(); +XEnumerationAccess xEnumAcc = (XEnumerationAccess)(UnoRuntime.queryInterface(XEnumerationAccess.class, xText)); +XEnumeration xEnum = xEnumAcc.createEnumeration(); +while (xEnum.hasMoreElements()) { + Object xObj = xEnum.nextElement(); + XServiceInfo xServiceInfo = (XServiceInfo) UnoRuntime.queryInterface(XServiceInfo.class, xObj); + if (xServiceInfo.supportsService("com.sun.star.text.Paragraph")) { + XPropertySet xSet = UnoRuntime.queryInterface(XPropertySet.class, xServiceInfo ); + // Set the justification to be center justified + xSet.setPropertyValue( "ParaAdjust", com.sun.star.style.ParagraphAdjust.CENTER ); + } +} + +return 0; diff --git a/scripting/examples/beanshell/Writer/InsertTable.bsh b/scripting/examples/beanshell/Writer/InsertTable.bsh new file mode 100644 index 000000000..099c45e68 --- /dev/null +++ b/scripting/examples/beanshell/Writer/InsertTable.bsh @@ -0,0 +1,32 @@ +/* + * 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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.lang.XMultiServiceFactory; + +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; +import com.sun.star.text.XTextContent; +import com.sun.star.text.XTextTable; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +XMultiServiceFactory xDocMSF = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, oDoc); +Object oTab = xDocMSF.createInstance("com.sun.star.text.TextTable"); +XTextTable xTextTable = (XTextTable)UnoRuntime.queryInterface(XTextTable.class, oTab); +xTextTable.initialize(4,3); // four rows, three columns +xTextContent = (XTextContent)UnoRuntime.queryInterface(XTextContent.class, xTextTable); + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xText = (XText) UnoRuntime.queryInterface(XText.class, xTextDoc.getText()); +xText.insertTextContent(xText.getEnd(), xTextContent, false); + +return 0; diff --git a/scripting/examples/beanshell/Writer/InsertText.bsh b/scripting/examples/beanshell/Writer/InsertText.bsh new file mode 100644 index 000000000..a1cfd3566 --- /dev/null +++ b/scripting/examples/beanshell/Writer/InsertText.bsh @@ -0,0 +1,28 @@ +/* + * 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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; + +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; +import com.sun.star.text.XTextRange; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +String sText = "This text is inserted before the existing text\n" + + "Here comes a second line\n"; + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xText = xTextDoc.getText(); +xTextRange = xText.getEnd(); +xTextRange.setString(sText); + +return 0; diff --git a/scripting/examples/beanshell/Writer/SetText.bsh b/scripting/examples/beanshell/Writer/SetText.bsh new file mode 100644 index 000000000..99f267c63 --- /dev/null +++ b/scripting/examples/beanshell/Writer/SetText.bsh @@ -0,0 +1,21 @@ +/* + * 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/. + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; + +import com.sun.star.text.XTextDocument; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xTextDoc.getText().setString("Hello from Beanshell!"); + +return 0; diff --git a/scripting/examples/beanshell/Writer/parcel-descriptor.xml b/scripting/examples/beanshell/Writer/parcel-descriptor.xml new file mode 100644 index 000000000..e0f034b3d --- /dev/null +++ b/scripting/examples/beanshell/Writer/parcel-descriptor.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><parcel xmlns:parcel="scripting.dtd" language="BeanShell"> +<script language="BeanShell"><locale lang="en"><displayname value="InsertText.bsh"/><description>InsertText.bsh</description></locale><logicalname value="InsertText.bsh"/><functionname value="InsertText.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="SetText.bsh"/><description>SetText.bsh</description></locale><logicalname value="SetText.bsh"/><functionname value="SetText.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="InsertTable.bsh"/><description>InsertTable.bsh</description></locale><logicalname value="InsertTable.bsh"/><functionname value="InsertTable.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="ChangeParaAdjust.bsh"/><description>ChangeParaAdjust.bsh</description></locale><logicalname value="ChangeParaAdjust.bsh"/><functionname value="ChangeParaAdjust.bsh"/></script><script language="BeanShell"><locale lang="en"><displayname value="ChangeFont.bsh"/><description>ChangeFont.bsh</description></locale><logicalname value="ChangeFont.bsh"/><functionname value="ChangeFont.bsh"/></script></parcel>
\ No newline at end of file diff --git a/scripting/examples/java/HelloWorld/HelloWorld.java b/scripting/examples/java/HelloWorld/HelloWorld.java new file mode 100644 index 000000000..1c960da32 --- /dev/null +++ b/scripting/examples/java/HelloWorld/HelloWorld.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XTextRange; +import com.sun.star.text.XText; +/** + * HelloWorld class + * + */ +public class HelloWorld { + public static void printHW(XScriptContext xSc) { + + // getting the text document object + XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface( + XTextDocument.class, xSc.getDocument()); + XText xText = xtextdocument.getText(); + XTextRange xTextRange = xText.getEnd(); + xTextRange.setString("Hello World (in Java)"); + + }// printHW + +} diff --git a/scripting/examples/java/HelloWorld/parcel-descriptor.xml b/scripting/examples/java/HelloWorld/parcel-descriptor.xml new file mode 100644 index 000000000..57e2e04c9 --- /dev/null +++ b/scripting/examples/java/HelloWorld/parcel-descriptor.xml @@ -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 . +--> +<parcel language="Java" xmlns:parcel="scripting.dtd"> + <script language="Java"> + <locale lang="en"> + <displayname value="HelloWorld.Java"/> + <description> + Prints "Hello World". + </description> + </locale> + <functionname value="org.libreoffice.example.java_scripts.HelloWorld.printHW"/> + <logicalname value="HelloWorld.printHW"/> + <languagedepprops> + <prop name="classpath" value="HelloWorld.jar"/> + </languagedepprops> + </script> +</parcel> diff --git a/scripting/examples/java/Highlight/HighlightText.java b/scripting/examples/java/Highlight/HighlightText.java new file mode 100644 index 000000000..f39ccbfa2 --- /dev/null +++ b/scripting/examples/java/Highlight/HighlightText.java @@ -0,0 +1,233 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.Type; +import com.sun.star.uno.AnyConverter; +import com.sun.star.text.XTextDocument; +import com.sun.star.beans.PropertyValue; +import com.sun.star.script.XLibraryContainer; +import com.sun.star.awt.*; +import com.sun.star.util.*; + +import java.awt.Color; + +public class HighlightText implements com.sun.star.awt.XActionListener { + + // UNO awt components of the Highlight dialog + XDialog findDialog = null; + XTextComponent findTextBox; + + // The document being searched + XTextDocument theDocument; + + // The text to be searched for + private String searchKey = ""; + + public void showForm(XScriptContext context) { + System.err.println("Starting showForm"); + + XMultiComponentFactory xmcf = + context.getComponentContext().getServiceManager(); + + Object[] args = new Object[1]; + args[0] = context.getDocument(); + + Object obj; + + try { + obj = xmcf.createInstanceWithArgumentsAndContext( + "com.sun.star.awt.DialogProvider", args, + context.getComponentContext()); + } catch (com.sun.star.uno.Exception e) { + System.err.println("Error getting DialogProvider object"); + return; + } + + XDialogProvider xDialogProvider = (XDialogProvider) + UnoRuntime.queryInterface(XDialogProvider.class, obj); + + System.err.println("Got DialogProvider, now get dialog"); + + try { + findDialog = xDialogProvider.createDialog( + "vnd.sun.star.script:" + + "ScriptBindingLibrary.Highlight?location=application"); + } catch (java.lang.Exception e) { + System.err.println("Got exception on first creating dialog: " + + e.getMessage()); + } + + if (findDialog == null) { + if (!tryLoadingLibrary(xmcf, context, "Dialog") || + !tryLoadingLibrary(xmcf, context, "Script")) { + System.err.println("Error loading ScriptBindingLibrary"); + return; + } + + try { + findDialog = xDialogProvider.createDialog( + "vnd.sun.star.script://" + + "ScriptBindingLibrary.Highlight?location=application"); + } catch (com.sun.star.lang.IllegalArgumentException iae) { + System.err.println("Error loading ScriptBindingLibrary"); + return; + } + } + + XControlContainer controls = (XControlContainer) + UnoRuntime.queryInterface(XControlContainer.class, findDialog); + + XButton highlightButton = (XButton) UnoRuntime.queryInterface( + XButton.class, controls.getControl("HighlightButton")); + highlightButton.setActionCommand("Highlight"); + + findTextBox = (XTextComponent) UnoRuntime.queryInterface( + XTextComponent.class, controls.getControl("HighlightTextField")); + + XButton exitButton = (XButton) UnoRuntime.queryInterface( + XButton.class, controls.getControl("ExitButton")); + exitButton.setActionCommand("Exit"); + + theDocument = (XTextDocument) UnoRuntime.queryInterface( + XTextDocument.class, context.getDocument()); + + highlightButton.addActionListener(this); + exitButton.addActionListener(this); + + findDialog.execute(); + } + + public void actionPerformed(ActionEvent e) { + if (e.ActionCommand.equals("Exit")) { + findDialog.endExecute(); + return; + } else if (e.ActionCommand.equals("Highlight")) { + searchKey = findTextBox.getText(); + + // highlight the text in red + Color cRed = new Color(255, 0, 0); + int red = cRed.getRGB(); + + XReplaceable replaceable = (XReplaceable) + UnoRuntime.queryInterface(XReplaceable.class, theDocument); + + XReplaceDescriptor descriptor = + (XReplaceDescriptor) replaceable.createReplaceDescriptor(); + + // Gets a XPropertyReplace object for altering the properties + // of the replaced text + XPropertyReplace xPropertyReplace = (XPropertyReplace) + UnoRuntime.queryInterface(XPropertyReplace.class, descriptor); + + // Sets the replaced text property fontweight value to Bold + PropertyValue wv = new PropertyValue("CharWeight", -1, + new Float(com.sun.star.awt.FontWeight.BOLD), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Sets the replaced text property color value to RGB parameter + PropertyValue cv = new PropertyValue("CharColor", -1, + Integer.valueOf(red), + com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Apply the properties + PropertyValue[] props = new PropertyValue[] { cv, wv }; + + try { + xPropertyReplace.setReplaceAttributes(props); + + // Only matches whole words and case sensitive + descriptor.setPropertyValue( + "SearchCaseSensitive", Boolean.TRUE); + descriptor.setPropertyValue("SearchWords", Boolean.TRUE); + } catch (com.sun.star.beans.UnknownPropertyException upe) { + System.err.println("Error setting up search properties"); + return; + } catch (com.sun.star.beans.PropertyVetoException pve) { + System.err.println("Error setting up search properties"); + return; + } catch (com.sun.star.lang.WrappedTargetException wte) { + System.err.println("Error setting up search properties"); + return; + } catch (com.sun.star.lang.IllegalArgumentException iae) { + System.err.println("Error setting up search properties"); + return; + } + + // Replaces all instances of searchKey with new Text properties + // and gets the number of instances of the searchKey + descriptor.setSearchString(searchKey); + descriptor.setReplaceString(searchKey); + replaceable.replaceAll(descriptor); + } + } + + public void disposing(EventObject o) { + // do nothing + } + + private boolean tryLoadingLibrary( + XMultiComponentFactory xmcf, XScriptContext context, String name) { + System.err.println("Try to load ScriptBindingLibrary"); + + try { + Object obj = xmcf.createInstanceWithContext( + "com.sun.star.script.Application" + name + "LibraryContainer", + context.getComponentContext()); + + XLibraryContainer xLibraryContainer = (XLibraryContainer) + UnoRuntime.queryInterface(XLibraryContainer.class, obj); + + System.err.println("Got XLibraryContainer"); + + Object serviceObj = context.getComponentContext().getValueByName( + "/singletons/com.sun.star.util.theMacroExpander"); + + XMacroExpander xme = (XMacroExpander) AnyConverter.toObject( + new Type(XMacroExpander.class), serviceObj); + + String bootstrapName = "bootstraprc"; + + if (System.getProperty("os.name").startsWith("Windows")) { + bootstrapName = "bootstrap.ini"; + } + + String libURL = xme.expandMacros( + "$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/basic/ScriptBindingLibrary/" + + name.toLowerCase() + ".xlb/"); + + System.err.println("libURL is: " + libURL); + + xLibraryContainer.createLibraryLink( + "ScriptBindingLibrary", libURL, false); + + System.err.println("liblink created"); + + } catch (com.sun.star.uno.Exception e) { + System.err.println("Got an exception loading lib: " + e.getMessage()); + return false; + } + + return true; + } +} diff --git a/scripting/examples/java/Highlight/parcel-descriptor.xml b/scripting/examples/java/Highlight/parcel-descriptor.xml new file mode 100644 index 000000000..bf3a86f94 --- /dev/null +++ b/scripting/examples/java/Highlight/parcel-descriptor.xml @@ -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 . +--> +<parcel language="Java" xmlns:parcel="scripting.dtd"> + <script language="Java"> + <locale lang="en"> + <displayname value="HighlightText.showForm"/> + <description> + Text highlighting + </description> + </locale> + <functionname value="org.libreoffice.example.java_scripts.HighlightText.showForm"/> + <logicalname value="HighlightText.showForm"/> + <languagedepprops> + <prop name="classpath" value="Highlight.jar"/> + </languagedepprops> + </script> +</parcel> diff --git a/scripting/examples/java/MemoryUsage/MemoryUsage.java b/scripting/examples/java/MemoryUsage/MemoryUsage.java new file mode 100644 index 000000000..a01d2bace --- /dev/null +++ b/scripting/examples/java/MemoryUsage/MemoryUsage.java @@ -0,0 +1,157 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import java.util.Random; +import java.util.Date; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.XInterface; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.document.XEmbeddedObjectSupplier; +import com.sun.star.awt.Rectangle; +import com.sun.star.beans.XPropertySet; +import com.sun.star.beans.PropertyValue; + +import com.sun.star.container.*; +import com.sun.star.chart.*; +import com.sun.star.table.*; +import com.sun.star.sheet.*; + +import com.sun.star.script.provider.XScriptContext; + +public class MemoryUsage { + public void updateMemoryUsage(XScriptContext ctxt) + throws Exception { + XSpreadsheet sheet = createSpreadsheet(ctxt); + + Runtime runtime = Runtime.getRuntime(); + Random generator = new Random(); + Date date = new Date(); + + // allocate a random amount of memory + int len = (int)(generator.nextFloat() * runtime.freeMemory() / 5); + byte[] bytes = new byte[len]; + + addData(sheet, date.toString(), + runtime.totalMemory(), runtime.freeMemory()); + + addChart(sheet); + } + + private XSpreadsheet createSpreadsheet(XScriptContext ctxt) + throws Exception { + XComponentLoader loader = (XComponentLoader) + UnoRuntime.queryInterface( + XComponentLoader.class, ctxt.getDesktop()); + + XComponent comp = loader.loadComponentFromURL( + "private:factory/scalc", "_blank", 4, new PropertyValue[0]); + + XSpreadsheetDocument doc = (XSpreadsheetDocument) + UnoRuntime.queryInterface(XSpreadsheetDocument.class, comp); + + XIndexAccess index = (XIndexAccess) + UnoRuntime.queryInterface(XIndexAccess.class, doc.getSheets()); + + XSpreadsheet sheet = (XSpreadsheet) AnyConverter.toObject( + new Type(com.sun.star.sheet.XSpreadsheet.class), index.getByIndex(0)); + + return sheet; + } + + private void addData( + XSpreadsheet sheet, String date, long total, long free) + throws Exception { + sheet.getCellByPosition(0, 0).setFormula("Used"); + sheet.getCellByPosition(0, 1).setFormula("Free"); + sheet.getCellByPosition(0, 2).setFormula("Total"); + + sheet.getCellByPosition(1, 0).setValue(total - free); + sheet.getCellByPosition(1, 1).setValue(free); + sheet.getCellByPosition(1, 2).setValue(total); + } + + private void addChart(XSpreadsheet sheet) + throws Exception { + Rectangle rect = new Rectangle(); + rect.X = 500; + rect.Y = 3000; + rect.Width = 10000; + rect.Height = 8000; + + XCellRange range = (XCellRange) + UnoRuntime.queryInterface(XCellRange.class, sheet); + + XCellRange myRange = + range.getCellRangeByName("A1:B2"); + + XCellRangeAddressable rangeAddr = (XCellRangeAddressable) + UnoRuntime.queryInterface(XCellRangeAddressable.class, myRange); + + CellRangeAddress myAddr = rangeAddr.getRangeAddress(); + + CellRangeAddress[] addr = new CellRangeAddress[1]; + addr[0] = myAddr; + + XTableChartsSupplier supp = (XTableChartsSupplier) + UnoRuntime.queryInterface(XTableChartsSupplier.class, sheet); + + XTableCharts charts = supp.getCharts(); + charts.addNewByName("Example", rect, addr, false, true); + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { } + + // get the diagram and Change some of the properties + XNameAccess chartsAccess = (XNameAccess) + UnoRuntime.queryInterface(XNameAccess.class, charts); + + XTableChart tchart = (XTableChart) + UnoRuntime.queryInterface( + XTableChart.class, chartsAccess.getByName("Example")); + + XEmbeddedObjectSupplier eos = (XEmbeddedObjectSupplier) + UnoRuntime.queryInterface(XEmbeddedObjectSupplier.class, tchart); + + XInterface xifc = eos.getEmbeddedObject(); + + XChartDocument xChart = (XChartDocument) + UnoRuntime.queryInterface(XChartDocument.class, xifc); + + XMultiServiceFactory xDocMSF = (XMultiServiceFactory) + UnoRuntime.queryInterface(XMultiServiceFactory.class, xChart); + + Object diagObject = + xDocMSF.createInstance("com.sun.star.chart.PieDiagram"); + + XDiagram xDiagram = (XDiagram) + UnoRuntime.queryInterface(XDiagram.class, diagObject); + + xChart.setDiagram(xDiagram); + + XPropertySet propset = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class, xChart.getTitle()); + propset.setPropertyValue("String", "JVM Memory Usage"); + } +} diff --git a/scripting/examples/java/MemoryUsage/parcel-descriptor.xml b/scripting/examples/java/MemoryUsage/parcel-descriptor.xml new file mode 100644 index 000000000..eb55ffe9a --- /dev/null +++ b/scripting/examples/java/MemoryUsage/parcel-descriptor.xml @@ -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 . +--> +<parcel language="Java" xmlns:parcel="scripting.dtd"> + <script language="Java"> + <locale lang="en"> + <displayname value="MemoryUtils.MemUsage"/> + <description> + Text highlighting + </description> + </locale> + <functionname value="org.libreoffice.example.java_scripts.MemoryUsage.updateMemoryUsage"/> + <logicalname value="MemoryUtils.MemUsage"/> + <languagedepprops> + <prop name="classpath" value="MemoryUsage.jar"/> + </languagedepprops> + </script> +</parcel> diff --git a/scripting/examples/java/Newsgroup/MimeConfiguration.java b/scripting/examples/java/Newsgroup/MimeConfiguration.java new file mode 100644 index 000000000..03b964834 --- /dev/null +++ b/scripting/examples/java/Newsgroup/MimeConfiguration.java @@ -0,0 +1,211 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.uno.XComponentContext; +import com.sun.star.script.framework.runtime.XScriptContext; +import com.sun.star.util.XStringSubstitution; + +import javax.mail.*; +import javax.activation.*; + +import java.io.*; + + +public class MimeConfiguration { + + // Office Installation path + private static String instPath = ""; + + + public static boolean createFiles(XScriptContext xsc) { + try { + XComponentContext xcc = xsc.getComponentContext(); + XMultiComponentFactory xmf = xcc.getServiceManager(); + + Object pathSub = + xmf.createInstanceWithContext("com.sun.star.comp.framework.PathSubstitution", + xcc); + XStringSubstitution stringSub = (XStringSubstitution) UnoRuntime.queryInterface( + XStringSubstitution.class, pathSub); + instPath = stringSub.getSubstituteVariableValue("$(inst)"); + + } catch (com.sun.star.beans.UnknownPropertyException upe) { + System.out.println("com.sun.star.beans.UnknownPropertyException"); + upe.printStackTrace(); + } catch (com.sun.star.uno.Exception e) { + System.out.println("com.sun.star.uno.Exception"); + e.printStackTrace(); + } + + writeMailCap(); + writeMimeTypes(); + + // ToDo: include status feedback to StatusWindow + return true; + } + + + + + private static void writeMailCap() { + String mailcapPath = getConfigDir() + System.getProperty("file.separator") + + "mailcap"; + + try { + if (! new File(java.net.URLDecoder.decode(mailcapPath)).exists()) { + File mailcapFile = new File(mailcapPath); + FileWriter out = new FileWriter(mailcapFile); + String[] lines = getMailcapText(); + + for (int i = 0; i < lines.length; i++) { + out.write(lines[i], 0, lines[i].length()); + } + + out.close(); + } else { + } + + + + // use prog dir, if not there then java.io to create/write new file + MailcapCommandMap map = new MailcapCommandMap(mailcapPath); + CommandMap.setDefaultCommandMap(map); + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + private static String[] getMailcapText() { + String[] mailcapText = { + "#\n", + "# Default mailcap file for the JavaMail System.\n", + "#\n", + "# JavaMail content-handlers:\n", + "#\n", + "text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain\n", + "text/html;; x-java-content-handler=com.sun.mail.handlers.text_html\n", + "text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml\n", + "image/gif;; x-java-content-handler=com.sun.mail.handlers.image_gif\n", + "image/jpeg;; x-java-content-handler=com.sun.mail.handlers.image_jpeg\n", + "multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed\n", + "message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822\n" + }; + + return mailcapText; + } + + + + private static void writeMimeTypes() { + String mimetypesPath = getConfigDir() + System.getProperty("file.separator") + + "mimetypes.default"; + + try { + if (! new File(java.net.URLDecoder.decode(mimetypesPath)).exists()) { + File mimetypesFile = new File(mimetypesPath); + FileWriter out = new FileWriter(mimetypesFile); + String[] lines = getMimeTypesText(); + + for (int i = 0; i < lines.length; i++) { + out.write(lines[i], 0, lines[i].length()); + } + + out.close(); + } else { + } + + MimetypesFileTypeMap mimeTypes = new MimetypesFileTypeMap(mimetypesPath); + FileTypeMap.setDefaultFileTypeMap(mimeTypes); + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + private static String[] getMimeTypesText() { + String[] mimesText = { + "#\n", + "# A simple, old format, mime.types file\n", + "#\n", + "text/html html htm HTML HTM\n", + "text/plain txt text TXT TEXT\n", + "image/gif gif GIF\n", + "image/ief ief\n", + "image/jpeg jpeg jpg jpe JPG\n", + "image/tiff tiff tif\n", + "image/x-xwindowdump xwd\n", + "application/postscript ai eps ps\n", + "application/rtf rtf\n", + "application/x-tex tex\n", + "application/x-texinfo texinfo texi\n", + "application/x-troff t tr roff\n", + "audio/basic au\n", + "audio/midi midi mid\n", + "audio/x-aifc aifc\n", + "audio/x-aiff aif aiff\n", + "audio/x-mpeg mpeg mpg\n", + "audio/x-wav wav\n", + "video/mpeg mpeg mpg mpe\n", + "video/quicktime qt mov\n", + "video/x-msvideo avi\n" + }; + + return mimesText; + } + + + private static String getConfigDir() { + // mailcap file must be written to the Office user/config directory + + // instPath is a URL, needs to be converted to a system pathname + String config = instPath + "/user/config"; + String configNonURL = ""; + + if (System.getProperty("os.name").indexOf("Windows") != -1) { + // Windows + // removes "file:///" + int start = 8; + configNonURL = config.substring(start, config.length()); + + // Convert forward to back-slashes + while (configNonURL.indexOf("/") != -1) { + int fSlash = configNonURL.indexOf("/"); + String firstPart = configNonURL.substring(0, fSlash); + String secondPart = configNonURL.substring(fSlash + 1, configNonURL.length()); + configNonURL = firstPart + "\\" + secondPart; + } + } else { + // Unix/Linux + // removes "file://" + int start = 7; + configNonURL = config.substring(start, config.length()); + } + + return configNonURL; + } + +} diff --git a/scripting/examples/java/Newsgroup/NewsGroup.java b/scripting/examples/java/Newsgroup/NewsGroup.java new file mode 100644 index 000000000..4c1708a05 --- /dev/null +++ b/scripting/examples/java/Newsgroup/NewsGroup.java @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +public class NewsGroup { + + private String hostname = ""; + private String newsgroupName = ""; + + public NewsGroup(String host, String group) { + hostname = host; + newsgroupName = group; + } + + public String getHostName() { + return hostname; + } + + public String getNewsgroupName() { + return newsgroupName; + } + +} diff --git a/scripting/examples/java/Newsgroup/OfficeAttachment.java b/scripting/examples/java/Newsgroup/OfficeAttachment.java new file mode 100644 index 000000000..75c0f4d45 --- /dev/null +++ b/scripting/examples/java/Newsgroup/OfficeAttachment.java @@ -0,0 +1,250 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +//import com.sun.star.frame.XComponentLoader; +import java.io.*; +import com.sun.star.lang.XComponent; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.frame.XStorable; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.frame.XModel; +import com.sun.star.script.framework.runtime.XScriptContext; + +// for debug only +import javax.swing.JOptionPane; + +public class OfficeAttachment { + + private StatusWindow status = null; + private XStorable storedDoc = null; + private File htmlFile = null; + private File officeFile = null; + private boolean isHtmlDoc = false; + private boolean isOfficeDoc = false; + private String templocationURL = ""; + private String templocationSystem = ""; + private String attachmentName = ""; + private String statusLine = ""; + + public OfficeAttachment(XScriptContext xsc, StatusWindow sw, boolean html, + boolean office) { + status = sw; + isHtmlDoc = html; + isOfficeDoc = office; + + templocationSystem = templocationURL = System.getProperty("user.home"); + + if (System.getProperty("os.name").indexOf("Windows") != -1) { + while (templocationURL.indexOf("\\") != -1) { + int sepPos = templocationURL.indexOf("\\"); + String firstPart = templocationURL.substring(0, sepPos); + String lastPart = templocationURL.substring(sepPos + 1, + templocationURL.length()); + templocationURL = firstPart + "/" + lastPart; + } + } + + try { + statusLine = "Querying Office for current document"; + status.setStatus(1, statusLine); + XScriptContext scriptcontext = xsc; + XModel xmodel = scriptcontext.getDocument(); + storedDoc = (XStorable) UnoRuntime.queryInterface(XStorable.class, xmodel); + // find document name from storedDoc + attachmentName = storedDoc.getLocation(); + } catch (Exception e) { + //UNO error + status.setStatus(1, "Error: " + statusLine); + } + + if (attachmentName.equalsIgnoreCase("")) { + attachmentName = "Attachment"; + } else { + int lastSep = attachmentName.lastIndexOf("/"); + attachmentName = attachmentName.substring(lastSep + 1, attachmentName.length()); + int dot = attachmentName.indexOf("."); + attachmentName = attachmentName.substring(0, dot); + } + } + + + public boolean createTempDocs() { + String filenameURL = "file:///" + templocationURL + "/" + attachmentName; + + try { + if (isHtmlDoc) { + statusLine = "Saving doc in HTML format"; + status.setStatus(4, statusLine); + PropertyValue[] propertyvalue_html = new PropertyValue[2]; + propertyvalue_html[0] = new PropertyValue(); + propertyvalue_html[0].Name = "Overwrite"; + propertyvalue_html[0].Value = Boolean.TRUE; + propertyvalue_html[1] = new PropertyValue(); + propertyvalue_html[1].Name = ("FilterName"); + propertyvalue_html[1].Value = "swriter: HTML (StarWriter)"; + storedDoc.storeAsURL(filenameURL + ".html", propertyvalue_html); + + File homedir = new File(templocationSystem); + File homefiles[] = homedir.listFiles(); + String file = ""; + + for (int i = 0; i < homefiles.length; i++) { + if (homefiles[i].getName().equals(attachmentName + ".html")) { + file = homefiles[i].getAbsolutePath(); + } + } + + htmlFile = new File(file); + } + + if (isOfficeDoc) { + statusLine = "Saving doc in .sxw format"; + status.setStatus(4, statusLine); + PropertyValue[] propertyvalue_sxw = new PropertyValue[2]; + propertyvalue_sxw[0] = new PropertyValue(); + propertyvalue_sxw[0].Name = "Overwrite"; + propertyvalue_sxw[0].Value = Boolean.TRUE; + propertyvalue_sxw[1] = new PropertyValue(); + propertyvalue_sxw[1].Name = "Overwrite"; + propertyvalue_sxw[1].Value = Boolean.TRUE; + storedDoc.storeAsURL(filenameURL + ".sxw", propertyvalue_sxw); + + File homedir = new File(templocationSystem); + + File homefiles[] = homedir.listFiles(); + String file = ""; + + for (int i = 0; i < homefiles.length; i++) { + if (homefiles[i].getName().equals(attachmentName + ".sxw")) { + file = homefiles[i].getAbsolutePath(); + } + } + + officeFile = new File(file); + } + + } catch (SecurityException se) { + status.setStatus(4, "Error: " + statusLine); + System.out.println("Security error while saving temporary Document(s). Check file permissions in home directory."); + se.printStackTrace(); + htmlFile = null; + officeFile = null; + return false; + } catch (Exception e) { + status.setStatus(4, "Error: " + statusLine); + System.out.println("Error saving temporary Document(s)"); + e.printStackTrace(); + htmlFile = null; + officeFile = null; + return false; + } + + return true; + } + + + public boolean removeTempDocs() { + /* + if( !htmlFile.exists() && !officeFile.exists() ) + { + System.out.println("Error: Document(s) have not been saved." ); + } + */ + + statusLine = "Removing temp docs"; + status.setStatus(13, statusLine); + + try { + if (isOfficeDoc && isHtmlDoc) { + htmlFile.delete(); + officeFile.delete(); + } else { + if (isOfficeDoc) { + officeFile.delete(); + } else { + htmlFile.delete(); + } + } + } catch (SecurityException se) { + status.setStatus(13, "Error: " + statusLine); + System.out.println("Security Error while deleting temporary Document(s). Check file permissions in home directory."); + se.printStackTrace(); + return false; + } + + return true; + } + + + public void cleanUpOnError() { + try { + if (isOfficeDoc && isHtmlDoc) { + htmlFile.delete(); + officeFile.delete(); + } else { + if (isOfficeDoc) { + officeFile.delete(); + } else { + htmlFile.delete(); + } + } + } catch (SecurityException se) { + System.out.println("Security Error while deleting temporary Document(s). Check file permissions in home directory."); + se.printStackTrace(); + } + } + + + public File[] getAttachments() { + + statusLine = "Retrieving temp docs"; + status.setStatus(8, statusLine); + + File attachments[] = null; + + if (isOfficeDoc && isHtmlDoc) { + attachments = new File[2]; + attachments[0] = htmlFile; + attachments[1] = officeFile; + } else { + if (isOfficeDoc) { + attachments = new File[1]; + attachments[0] = officeFile; + } else { + attachments = new File[1]; + attachments[0] = htmlFile; + } + } + + return attachments; + } + + + public boolean isHtmlAttachment() { + return isHtmlDoc; + } + + + public boolean isOfficeAttachment() { + return isOfficeDoc; + } + +} diff --git a/scripting/examples/java/Newsgroup/PostNewsgroup.java b/scripting/examples/java/Newsgroup/PostNewsgroup.java new file mode 100644 index 000000000..6c6ecdf37 --- /dev/null +++ b/scripting/examples/java/Newsgroup/PostNewsgroup.java @@ -0,0 +1,633 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.border.*; +import java.util.Vector; +import com.sun.star.script.framework.runtime.XScriptContext; + + +public class PostNewsgroup extends JFrame { + + // Post to newsgroup objects + private NewsGroup[] subscribedNewsgroups = null; + private XScriptContext xscriptcontext = null; + + private final int FRAMEX = 300; + private final int FRAMEY = 300; + private final int TEXTBOXWIDTH = 300; + private final int TEXTBOXHEIGHT = 24; + private final int TEXTAREAHEIGHT = 70; + private final int BUTTONWIDTH = 80; + private final int BUTTONHEIGHT = 30; + + private PostNewsgroup window = null; + private JComboBox newsgroupComboBox = null; + private JTextField hostTextField = null; + private JTextField replyTextField = null; + private JTextField subjectTextField = null; + private JTextArea commentTextArea = null; + private JRadioButton officeHtmlButton = null; + private JRadioButton officeButton = null; + private JRadioButton htmlButton = null; + private JButton postButton = null; + private JButton cancelButton = null; + + // JFrame for launch progress dialog + private StatusWindow statusWindow = null; + private String statusLine = ""; + + // Tool tip text + private final String newsgroupText = "Newsgroup name"; + private final String hostText = "Newsgroup host/server name"; + private final String replyText = "Email address to reply to"; + private final String subjectText = "Subject title for the mail"; + private final String commentText = "Additional comment on mail"; + private final String officeHtmlText = + "Post as both Office and HTML attachments"; + private final String officeText = "Post as Office attachment only"; + private final String htmlText = "Post as HTML attachment only"; + private final String postText = "Post to newsgroup"; + private final String cancelText = "Cancel post to newsgroup"; + + + public void post(XScriptContext xsc) { + xscriptcontext = xsc; + window = this; + + // create mailcap and mimetypes files (fix for classloader problem) + MimeConfiguration.createFiles(xscriptcontext); + + this.setTitle("Post Document To Newsgroup"); + this.setLocation(FRAMEX, FRAMEY); + + this.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent event) { + System.out.println("Focus gained"); + window.update(window.getGraphics()); + } + + public void focusLost(FocusEvent event) { + System.out.println("Focus lost"); + } + }); + + Container container = getContentPane(); + container.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.BOTH; + + JPanel labelPanel = constructLabelPanel(); + JPanel textPanel = constructTextPanel(); + JPanel optionPanel = constructOptionPanel(); + JPanel buttonPanel = constructButtonPanel(); + + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 3; + constraints.insets = new Insets(15, 15, 5, 5); + container.add(labelPanel, constraints); + + constraints.gridx = 1; + constraints.gridy = 0; + constraints.gridwidth = 4; + constraints.gridheight = 3; + constraints.insets = new Insets(15, 5, 5, 15); + container.add(textPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 3; + constraints.gridwidth = 5; + constraints.gridheight = 1; + constraints.insets = new Insets(5, 15, 5, 15); + container.add(optionPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 4; + constraints.gridwidth = 5; + constraints.gridheight = 1; + constraints.insets = new Insets(5, 5, 5, 5); + container.add(buttonPanel, constraints); + + this.pack(); + this.setResizable(false); + this.setVisible(true); + } + + + private JPanel constructLabelPanel() { + JLabel newsgroupLabel = new JLabel("Newsgroup:"); + JLabel hostLabel = new JLabel("Host:"); + JLabel replyLabel = new JLabel("Reply:"); + JLabel subjectLabel = new JLabel("Subject:"); + JLabel commentLabel = new JLabel("Comment:"); + + newsgroupLabel.setToolTipText(newsgroupText); + hostLabel.setToolTipText(hostText); + replyLabel.setToolTipText(replyText); + subjectLabel.setToolTipText(subjectText); + commentLabel.setToolTipText(commentText); + + JPanel newsgroupPanel = new JPanel(); + newsgroupPanel.setLayout(new BorderLayout()); + newsgroupPanel.add(newsgroupLabel, "West"); + JPanel hostPanel = new JPanel(); + hostPanel.setLayout(new BorderLayout()); + hostPanel.add(hostLabel, "West"); + JPanel replyPanel = new JPanel(); + replyPanel.setLayout(new BorderLayout()); + replyPanel.add(replyLabel, "West"); + JPanel subjectPanel = new JPanel(); + subjectPanel.setLayout(new BorderLayout()); + subjectPanel.add(subjectLabel, "West"); + JPanel commentPanel = new JPanel(); + commentPanel.setLayout(new BorderLayout()); + commentPanel.add(commentLabel, "West"); + JPanel emptyPanel = new JPanel(); + + final int labelWidth = 80; + newsgroupPanel.setPreferredSize(new Dimension(labelWidth, TEXTBOXHEIGHT)); + hostPanel.setPreferredSize(new Dimension(labelWidth, TEXTBOXHEIGHT)); + replyPanel.setPreferredSize(new Dimension(labelWidth, TEXTBOXHEIGHT)); + subjectPanel.setPreferredSize(new Dimension(labelWidth, TEXTBOXHEIGHT)); + commentPanel.setPreferredSize(new Dimension(labelWidth, TEXTBOXHEIGHT)); + + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.BOTH; + constraints.insets = new Insets(5, 5, 5, 5); + + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.weightx = constraints.weighty = 0.0; + panel.add(newsgroupPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(hostPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 2; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(replyPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 3; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(subjectPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 4; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(commentPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 5; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.weightx = constraints.weighty = 1.0; + panel.add(emptyPanel, constraints); + + return panel; + } + + + private JPanel constructTextPanel() { + hostTextField = new JTextField(); + hostTextField.setPreferredSize(new Dimension(TEXTBOXWIDTH, TEXTBOXHEIGHT)); + hostTextField.setToolTipText(hostText); + hostTextField.setBorder(new EtchedBorder()); + + //optionPanel.setBorder( new TitledBorder( new EtchedBorder(), "Document Format" ) ); + newsgroupComboBox = getNewsgroupCombo(); + + replyTextField = new JTextField(); + replyTextField.setPreferredSize(new Dimension(TEXTBOXWIDTH, TEXTBOXHEIGHT)); + replyTextField.setToolTipText(replyText); + replyTextField.setBorder(new EtchedBorder()); + + subjectTextField = new JTextField(); + subjectTextField.setPreferredSize(new Dimension(TEXTBOXWIDTH, TEXTBOXHEIGHT)); + subjectTextField.setToolTipText(subjectText); + subjectTextField.setBorder(new EtchedBorder()); + + commentTextArea = new JTextArea(); + commentTextArea.setPreferredSize(new Dimension(TEXTBOXWIDTH, TEXTAREAHEIGHT)); + commentTextArea.setToolTipText(commentText); + commentTextArea.setBorder(new EtchedBorder()); + + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.BOTH; + constraints.insets = new Insets(5, 5, 5, 5); + + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(newsgroupComboBox, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(hostTextField, constraints); + + constraints.gridx = 0; + constraints.gridy = 2; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(replyTextField, constraints); + + constraints.gridx = 0; + constraints.gridy = 3; + constraints.gridwidth = 1; + constraints.gridheight = 1; + panel.add(subjectTextField, constraints); + + constraints.gridx = 0; + constraints.gridy = 4; + constraints.gridwidth = 1; + constraints.gridheight = 2; + panel.add(commentTextArea, constraints); + + return panel; + } + + + private JComboBox getNewsgroupCombo() { + newsgroupComboBox = new JComboBox(); + //newsgroupComboBox.setBorder( new EtchedBorder() ); + + newsgroupComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + // when newsgroup is selected + if (subscribedNewsgroups != null) { + int position = newsgroupComboBox.getSelectedIndex(); + + if (position != -1) { + hostTextField.setText(subscribedNewsgroups[ position ].getHostName()); + newsgroupComboBox.setToolTipText("Newsgroup name: " + + subscribedNewsgroups[ position ].getNewsgroupName() + " (Host name: " + + subscribedNewsgroups[ position ].getHostName() + ")"); + } + } + } + }); + + NewsGroup groupToSend = null; + SubscribedNewsgroups newsgroups = new SubscribedNewsgroups(); + subscribedNewsgroups = newsgroups.getNewsGroups(); + + // Test for no .mozilla or no subscribed newsgroups + // subscribedNewsgroups = null; + + if (subscribedNewsgroups == null) { + JOptionPane.showMessageDialog(window, + "No subscribed newsgroups found in mozilla/netscape profile \nPlease enter newsgroup and host name", + "Newsgroups Information", JOptionPane.INFORMATION_MESSAGE); + } else { + // Copy all newsgroups into a vector for comparison + // Alter entries (to include host name) if duplication is found + ArrayList vector = new ArrayList(subscribedNewsgroups.length); + + for (int i = 0; i < subscribedNewsgroups.length; i++) { + vector.add(subscribedNewsgroups[i].getNewsgroupName()); + } + + // Compare and alter + for (int i = 0; i < subscribedNewsgroups.length; i++) { + // check if combo box already has a newsgroup with same name + // then add host name to differentiate + for (int j = 0; j < subscribedNewsgroups.length; j++) { + if (j != i + && subscribedNewsgroups[j].getNewsgroupName().equalsIgnoreCase( + subscribedNewsgroups[i].getNewsgroupName())) { + vector.set(j, subscribedNewsgroups[j].getNewsgroupName() + " (" + + subscribedNewsgroups[j].getHostName() + ")"); + vector.set(i, subscribedNewsgroups[i].getNewsgroupName() + " (" + + subscribedNewsgroups[i].getHostName() + ")"); + } + } + } + + // Copy converted newsgroups from vector to combo box + for (int i = 0; i < subscribedNewsgroups.length; i++) { + newsgroupComboBox.addItem(vector.elementAt(i)); + } + }// else + + newsgroupComboBox.setPreferredSize(new Dimension(TEXTBOXWIDTH, TEXTBOXHEIGHT)); + newsgroupComboBox.setEditable(true); + + return newsgroupComboBox; + } + + + + private JPanel constructOptionPanel() { + officeHtmlButton = new JRadioButton("Office and HTML", true); + officeHtmlButton.setToolTipText(officeHtmlText); + + officeButton = new JRadioButton("Office"); + officeButton.setToolTipText(officeText); + + htmlButton = new JRadioButton("HTML"); + htmlButton.setToolTipText(htmlText); + + JRadioButton[] rbuttons = { officeHtmlButton, officeButton, htmlButton }; + ButtonGroup radioButtonGroup = new ButtonGroup(); + + for (int i = 0; i < rbuttons.length; i++) { + radioButtonGroup.add(rbuttons[i]); + } + + JPanel optionPanel = new JPanel(); + optionPanel.setBorder(new TitledBorder(new EtchedBorder(), "Document Format")); + optionPanel.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.BOTH; + + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(5, 5, 5, 30); + optionPanel.add(officeHtmlButton, constraints); + + constraints.gridx = 1; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(5, 20, 5, 30); + optionPanel.add(officeButton, constraints); + + constraints.gridx = 2; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(5, 20, 5, 5); + optionPanel.add(htmlButton, constraints); + + return optionPanel; + } + + + + public boolean sendingActions() { + // posting actions + // Validate the data + if (isValidData()) { + // Create status window + StatusWindow statusWindow = new StatusWindow(window, "Posting to Newsgroup", + FRAMEX, FRAMEY); + + statusWindow.setVisible(true); + statusLine = "Ready to send..."; + statusWindow.setStatus(0, statusLine); + + // Get the boolean values for HTML/Office document + // params: ( XScriptContext, StatusWindow, html document, office document ) + + boolean html = false; + boolean office = false; + + if (officeHtmlButton.isSelected()) { + html = true; + office = true; + } + + if (officeButton.isSelected()) { + office = true; + html = false; + } + + if (htmlButton.isSelected()) { + html = true; + office = false; + } + + OfficeAttachment officeAttach = new OfficeAttachment(xscriptcontext, + statusWindow, html, office); + + statusLine = "Getting user input"; + statusWindow.setStatus(2, statusLine); + // Get replyto, subject, comment from textboxes + String replyto = replyTextField.getText(); + String subject = subjectTextField.getText(); + String comment = commentTextArea.getText(); + + // Get newsgroup from combo box (corresponding position) + String host = ""; + String group = ""; + int position = newsgroupComboBox.getSelectedIndex(); + + if (subscribedNewsgroups == null || position == -1) { + host = hostTextField.getText(); + group = newsgroupComboBox.getSelectedItem().toString(); + } else { + host = subscribedNewsgroups[ position ].getHostName(); + group = subscribedNewsgroups[ position ].getNewsgroupName(); + } + + statusLine = "Creating sender object"; + statusWindow.setStatus(3, statusLine); + Sender sender = new Sender(statusWindow, officeAttach, replyto, subject, + comment, host, group); + + if (!sender.sendMail()) { + statusWindow.enableCancelButton(true); + officeAttach.cleanUpOnError(); + return false; + } + + statusLine = "Send is complete"; + statusWindow.setStatus(14, statusLine); + } else { + return false; + } + + return true; + } + + + private JPanel constructButtonPanel() { + Action postAction = new AbstractAction() { + public void actionPerformed(ActionEvent event) { + // posting actions + sendingActions(); + }// actionPerformed + }; + + Action cancelAction = new AbstractAction() { + public void actionPerformed(ActionEvent event) { + // cancelling actions + window.dispose(); + } + }; + + postButton = new JButton(); + postButton.setAction(postAction); + postButton.setToolTipText(postText); + postButton.setText("Post"); + postButton.setPreferredSize(new Dimension(BUTTONWIDTH + 20, BUTTONHEIGHT)); + + cancelButton = new JButton(); + cancelButton.setAction(cancelAction); + cancelButton.setToolTipText(cancelText); + cancelButton.setText("Cancel"); + cancelButton.setPreferredSize(new Dimension(BUTTONWIDTH + 20, BUTTONHEIGHT)); + + JSeparator sep = new JSeparator(SwingConstants.HORIZONTAL); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.BOTH; + constraints.insets = new Insets(5, 5, 5, 5); + + JPanel emptyPanel1 = new JPanel(); + emptyPanel1.setPreferredSize(new Dimension(BUTTONWIDTH, BUTTONHEIGHT)); + + JPanel emptyPanel2 = new JPanel(); + emptyPanel2.setPreferredSize(new Dimension(BUTTONWIDTH, BUTTONHEIGHT)); + + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 4; + constraints.gridheight = 1; + buttonPanel.add(sep, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + buttonPanel.add(emptyPanel1, constraints); + + constraints.gridx = 1; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + buttonPanel.add(emptyPanel2, constraints); + + constraints.gridx = 2; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + buttonPanel.add(postButton, constraints); + + constraints.gridx = 3; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(5, 5, 5, 0); + buttonPanel.add(cancelButton, constraints); + + return buttonPanel; + } + + + public void enableButtons(boolean enable) { + if (enable) { + postButton.setEnabled(true); + cancelButton.setEnabled(true); + } else { + postButton.setEnabled(false); + cancelButton.setEnabled(false); + } + } + + + private boolean isValidData() { + // newsgroupComboBox must not be blank (format? dots and whitespace) + String newsgroupString = ""; + int position = newsgroupComboBox.getSelectedIndex(); + + if (subscribedNewsgroups == null || position == -1) { + newsgroupString = newsgroupComboBox.getSelectedItem().toString(); + } else { + newsgroupString = subscribedNewsgroups[ position ].getNewsgroupName(); + } + + if (newsgroupString.length() == 0) { + newsgroupComboBox.requestFocus(); + JOptionPane.showMessageDialog(window, "Please enter a newsgroup name", + "Input Error", JOptionPane.ERROR_MESSAGE); + return false; + } + + + // hostTextField must not be blank (format?) + String hostString = hostTextField.getText(); + + if (hostString.length() == 0) { + hostTextField.requestFocus(); + JOptionPane.showMessageDialog(window, "Please enter a hostname", "Input Error", + JOptionPane.ERROR_MESSAGE); + return false; + } + + + // replyTextField must have <string>@<string>.<string> + // (string at least 2 chars long) + // consider <s>.<s>@<s>.<s>.<s> format? (array of dot positions?) + String replyString = replyTextField.getText(); + int atPos = replyString.indexOf("@"); + int dotPos = replyString.lastIndexOf("."); + int length = replyString.length(); + + if (length == 0 || atPos == -1 || dotPos == -1 || atPos < 2 || dotPos < atPos + || dotPos + 2 == length || atPos + 2 == dotPos + || atPos != replyString.lastIndexOf("@") || replyString.indexOf(" ") != -1) { + replyTextField.requestFocus(); + JOptionPane.showMessageDialog(window, + "Please enter a valid reply to email address", "Input Error", + JOptionPane.ERROR_MESSAGE); + return false; + } + + + // subjectTextField must not be blank? + String subjectString = subjectTextField.getText(); + + if (subjectString.length() == 0) { + subjectTextField.requestFocus(); + JOptionPane.showMessageDialog(window, "Please enter subject title", + "Input Error", JOptionPane.ERROR_MESSAGE); + return false; + } + + // details are valid + return true; + } + +} diff --git a/scripting/examples/java/Newsgroup/Sender.java b/scripting/examples/java/Newsgroup/Sender.java new file mode 100644 index 000000000..00b3db87b --- /dev/null +++ b/scripting/examples/java/Newsgroup/Sender.java @@ -0,0 +1,141 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import javax.mail.*; +import javax.mail.internet.*; +import com.msoft.mail.provider.nntp.NNTPTransport; +import java.util.Properties; +import java.io.*; +import javax.activation.*; + + +public class Sender { + // Constructor params: + private StatusWindow status = null; + private OfficeAttachment attachments = null; + private String replyto = ""; + private String subject = ""; + private String comment = ""; + private String hostname = ""; + private String newsgroup = ""; + private String statusLine = ""; + + + + public Sender(StatusWindow sw, OfficeAttachment attach, String reply, + String sub, String com, String host, String group) { + status = sw; + attachments = attach; + replyto = reply; + subject = sub; + comment = com; + hostname = host; + newsgroup = group; + } + + + + public boolean sendMail() { + int statusPos = 5; + + try { + attachments.createTempDocs(); + // Property for any information + Properties props = new Properties(); + + // Create unique session (null is unused authenticator info) + statusLine = "Creating unique session"; + status.setStatus(statusPos, statusLine); // 5 + Session session = Session.getInstance(props, null); + + // Create message + statusPos++; // 6 + statusLine = "Creating message"; + status.setStatus(statusPos, statusLine); + MimeMessage message = new MimeMessage(session); + message.setFrom(new InternetAddress(replyto)); + message.setSubject(subject); + message.setText(comment); + message.addHeader("Newsgroups", newsgroup); + + // Buildup bodypart with text and attachments + Multipart multipart = new MimeMultipart(); + + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setText(comment); + multipart.addBodyPart(messageBodyPart); + + statusPos++; // 7 + statusLine = "Adding attachment(s)"; + status.setStatus(statusPos, statusLine); + File attachs[] = attachments.getAttachments(); + + for (int i = 0; i < attachs.length; i++) { + messageBodyPart = new MimeBodyPart(); + DataSource filesource = new FileDataSource(attachs[i]); + messageBodyPart.setDataHandler(new DataHandler(filesource)); + messageBodyPart.setFileName(attachs[i].getName()); + multipart.addBodyPart(messageBodyPart); + } + + // Add multipart to mail + message.setContent(multipart); + + // Create and send NNTP transport + statusPos += 2; // 9 + statusLine = "Creating NNTP transport"; + status.setStatus(statusPos, statusLine); + Transport transport = new NNTPTransport(session, + new URLName("news:" + newsgroup)); + + // Null parameters are for user name and password + statusPos++; // 10 + statusLine = "Connecting to mail server"; + status.setStatus(statusPos, statusLine); + transport.connect(hostname, null, null); + + statusPos++; // 11 + statusLine = "Sending message"; + status.setStatus(statusPos, statusLine); + transport.sendMessage(message, message.getAllRecipients()); + + statusPos++; // 12 + statusLine = "Closing transport"; + status.setStatus(statusPos, statusLine); + transport.close(); + + // Clean up when finished + attachments.removeTempDocs(); + + return true; + } catch (MessagingException me) { + if (statusPos == 10) { + statusLine = "Error connecting (User authentication?)"; + } + + status.setStatus(statusPos, statusLine); + System.out.println("Error sending message: "); + me.printStackTrace(); + return false; + } + + } + +} diff --git a/scripting/examples/java/Newsgroup/StatusWindow.java b/scripting/examples/java/Newsgroup/StatusWindow.java new file mode 100644 index 000000000..7b37c8fa7 --- /dev/null +++ b/scripting/examples/java/Newsgroup/StatusWindow.java @@ -0,0 +1,147 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.border.*; + +public class StatusWindow extends JFrame { + + private JProgressBar progressBar = null; + private JTextField statusLabel = null; + private JButton cancelButton = null; + private JFrame statusWindow = null; + private PostNewsgroup mainWindow = null; + + private final int MAXPROGRESS = 13; + private final int MINPROGRESS = 0; + + + public StatusWindow(PostNewsgroup mw, String title, int parentX, int parentY) { + this.setTitle(title); + this.setLocation(parentX + 100, parentY + 100); + statusWindow = this; + mainWindow = mw; + + mainWindow.enableButtons(false); + + statusWindow.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent event) { + mainWindow.enableButtons(true); + } + }); + + progressBar = new JProgressBar(); + progressBar.setStringPainted(true); + progressBar.setMaximum(MAXPROGRESS); + progressBar.setMinimum(MINPROGRESS); + progressBar.setSize(30, 400); + + JLabel progLabel = new JLabel("Progress:"); + + JPanel progressPanel = new JPanel(); + progressPanel.setLayout(new BorderLayout(10, 0)); + progressPanel.add(progLabel, "West"); + progressPanel.add(progressBar, "East"); + + statusLabel = new JTextField(); + statusLabel.setColumns(25); + statusLabel.setEditable(false); + statusLabel.setBorder(null); + JPanel statusPanel = new JPanel(); + statusPanel.setLayout(new BorderLayout()); + statusPanel.add(statusLabel, "West"); + + cancelButton = new JButton("Cancel"); + cancelButton.setSize(30, 100); + cancelButton.setEnabled(false); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + // cancelling actions + mainWindow.enableButtons(true); + statusWindow.dispose(); + } + }); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new BorderLayout(0, 5)); + buttonPanel.add(cancelButton, "East"); + buttonPanel.add(new JSeparator(SwingConstants.HORIZONTAL), "North"); + + Container container = getContentPane(); + container.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.BOTH; + + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(15, 15, 10, 15); + container.add(progressPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(10, 15, 10, 15); + container.add(statusPanel, constraints); + + constraints.gridx = 0; + constraints.gridy = 2; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.insets = new Insets(10, 15, 5, 15); + container.add(buttonPanel, constraints); + + this.pack(); + this.setResizable(false); + + } + + + public void setStatus(int progress, String status) { + progressBar.setValue(progress); + statusLabel.setText(status); + statusLabel.setToolTipText(status); + + if (progress == MAXPROGRESS) { + cancelButton.setEnabled(true); + cancelButton.setText("Close"); + } + + update(getGraphics()); + mainWindow.update(mainWindow.getGraphics()); + } + + + public void enableCancelButton(boolean enable) { + if (enable) { + cancelButton.setEnabled(true); + cancelButton.setText("Finish"); + } else { + cancelButton.setEnabled(false); + cancelButton.setText("Cancel"); + } + + } + +} diff --git a/scripting/examples/java/Newsgroup/SubscribedNewsgroups.java b/scripting/examples/java/Newsgroup/SubscribedNewsgroups.java new file mode 100644 index 000000000..eb135a3e8 --- /dev/null +++ b/scripting/examples/java/Newsgroup/SubscribedNewsgroups.java @@ -0,0 +1,349 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import java.io.*; +import java.util.Vector; + + +public class SubscribedNewsgroups { + + + private static NewsGroup[] allSubscribed = null; + private static boolean windows = false; + + public static void main(String[] args) { + // Test the class + SubscribedNewsgroups subscribed = new SubscribedNewsgroups(); + + NewsGroup allGroups[] = subscribed.getNewsGroups(); + + if (allGroups == null) { + System.out.println("Could not find subscribed newsgroups from mozilla/netscape mailrc files"); + } else { + for (int i = 0; i < allGroups.length; i++) { + System.out.println("Hostname is: " + allGroups[i].getHostName() + + " Newsgroup is: " + allGroups[i].getNewsgroupName()); + } + } + } + + + + // Only public method of the class + // Returns and array of unique NewsGroup objects + public NewsGroup[] getNewsGroups() { + windows = false; + + if (System.getProperty("os.name").indexOf("Windows") != -1) { + windows = true; + } + + String mozillaHome = ""; + + if (windows) { + mozillaHome = System.getProperty("user.home") + + System.getProperty("file.separator") + "Application Data" + + System.getProperty("file.separator") + "Mozilla" + + System.getProperty("file.separator") + "Profiles"; + } else { + mozillaHome = System.getProperty("user.home") + + System.getProperty("file.separator") + ".mozilla"; + } + + if (!new File(mozillaHome).isDirectory()) { + return null; + } + + // Get all the profiles belonging to the user + File profiles[] = findProfiles(new File(mozillaHome)); + + if (profiles.length < 1) { + return null; + } + + // Get the News directory for each profile + File allNewsDirs[] = new File[ profiles.length ]; + + for (int i = 0; i < profiles.length; i++) { + File newsDir = findNewsDir(profiles[i]); + allNewsDirs[i] = newsDir; + } + + // Check that at least one News directory exists and remove nulls + boolean newsFound = false; + + for (int i = 0; i < allNewsDirs.length; i++) { + if (allNewsDirs[i] != null) { + newsFound = true; + break; + } + } + + if (!newsFound) { + return null; + } + + // Get all the mailrc files for each News directory + File allMailrcs[] = findMailrcFiles(allNewsDirs); + + if (allMailrcs == null) { + return null; + } + + ArrayList<NewsGroup> subscribed = new ArrayList<NewsGroup>(); + + // Get the newsgroups in each mailrc file + for (int i = 0; i < allMailrcs.length; i++) { + File mailrc = (File) allMailrcs[i]; + NewsGroup newsgroup[] = findNewsgroups(mailrc); + + //if the Newsgroup has not already been added to the list + for (int j = 0; j < newsgroup.length; j++) { + // if newsgroup is unique then add to the list + if (!listed(newsgroup[j], subscribed)) { + subscribed.addElement(newsgroup[j]); + } + } + } + + // Copy all unique Newsgroups into the global array + allSubscribed = new NewsGroup[ subscribed.size() ]; + subscribed.toArray(allSubscribed); + + // Test that at least one subscribed newsgroup has been found + if (allSubscribed.length < 1) { + return null; + } + + return allSubscribed; + } + + + + + // Tests if the NewsGroup object has already been listed by another mailrc file + private static boolean listed(NewsGroup newsgroup, + ArrayList<NewsGroup> uniqueSubscription) { + for (int i = 0; i < uniqueSubscription.size(); i++) { + NewsGroup tempGroup = uniqueSubscription.elementAt(i); + + // Test for duplication + if (newsgroup.getHostName().equalsIgnoreCase(tempGroup.getHostName()) && + newsgroup.getNewsgroupName().equalsIgnoreCase(tempGroup.getNewsgroupName())) + return true; + } + + return false; + } + + + + + // Finds all the NewsGroups in an individual mailrc file + private static NewsGroup[] findNewsgroups(File mailrcfile) { + + String hostname = ""; + String newsgroup = ""; + NewsGroup mailrcNewsGroups[] = null; + + //Retrieve name of news host/server from file name + //Sequentially access each of the newsgroups + //If the newsgroup is not already contained in the global NewsGroup[] array then add it + + String filename = mailrcfile.getPath(); + + if (windows) { + // Windows format "staroffice-news.germany.sun.com.rc" + int hostNameStart = filename.lastIndexOf("\\") + 1; + int hostNameEnd = filename.indexOf(".rc"); + hostname = filename.substring(hostNameStart, hostNameEnd); + } else { + // Unix/Linux format "newsrc-staroffice-news.germany.sun.com" + int hostNameStart = filename.lastIndexOf("newsrc-") + 7; + hostname = filename.substring(hostNameStart, filename.length()); + } + + // Assumes the content format in Window is the same as Unix/Linux (unknown at the moment) + // i.e. a list of newsgroups each ending with a ":" + LineNumberReader in = null; + + try { + in = new LineNumberReader(new FileReader(mailrcfile)); + ArrayList groups = new ArrayList(); + String inString = ""; + int line = 0; + + while (inString != null) { + in.setLineNumber(line); + inString = in.readLine(); + line++; + + if (inString != null) { + int newsgroupEnd = inString.indexOf(":"); + newsgroup = inString.substring(0, newsgroupEnd); + NewsGroup group = new NewsGroup(hostname, newsgroup); + groups.addElement(group); + } + } + + mailrcNewsGroups = new NewsGroup[ groups.size() ]; + groups.copyInto(mailrcNewsGroups); + in.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + return mailrcNewsGroups; + } + + + // Finds all the mailrc files for all the given News directories + private static File[] findMailrcFiles(File[] newsDirs) { + ArrayList allFiles = new ArrayList(); + + for (int i = 0; i < newsDirs.length; i++) { + if (newsDirs[i] != null) { + File mailrcFiles[] = newsDirs[i].listFiles(new VersionFilter()); + + if (mailrcFiles != null) { + for (int j = 0; j < mailrcFiles.length; j++) { + allFiles.addElement(mailrcFiles[j]); + } + } + } + } + + File allMailrcFiles[] = new File[ allFiles.size() ]; + allFiles.copyInto(allMailrcFiles); + + if (allMailrcFiles.length == 0) { + return null; + } + + return allMailrcFiles; + } + + + // Finds all profiles belonging to one user (can be more than one) + private static File[] findProfiles(File start) { + // Get all files and directories in .mozilla + File allFiles[] = start.listFiles(); + File[] dirs = new File[allFiles.length]; + int dirCounter = 0; + + // Remove files leaving directories only + for (int i = 0; i < allFiles.length; i++) { + if (allFiles[i].isDirectory()) { + dirs[dirCounter] = allFiles[i]; + dirCounter++; + } + } + + // Add each directory to a user profile array + File[] profileDirs = new File[dirCounter]; + + for (int i = 0; i < dirCounter; i++) { + profileDirs[i] = dirs[i]; + } + + // return a File array containing the profile dirs + return profileDirs; + } + + + // Recursively searches for the News directory for a given profile directory + private static File findNewsDir(File start) { + File mailrcFile = null; + + // File array containing all matches for the version filter ("News") + File files[] = start.listFiles(new VersionFilter()); + + // If the array is empty then no matches were found + if (files.length == 0) { + // File array of all the directories in File start + File dirs[] = start.listFiles(new DirFilter()); + + // for each of the directories check for a match + for (int i = 0; i < dirs.length; i++) { + mailrcFile = findNewsDir(dirs[i]); + + if (mailrcFile != null) { + // break the for loop + break; + } + } + } else { + // end recursion + // Check for a News directory inside the News directory (fix for bug) + // Original solution had only "mailrcFile = files[0];" + + boolean noChildNews = true; + File checkChildNewsDirs[] = files[0].listFiles(new VersionFilter()); + + if (checkChildNewsDirs != null) { + for (int i = 0; i < checkChildNewsDirs.length; i++) { + if (checkChildNewsDirs[i].getName().equals("News")) { + noChildNews = false; + break; + } + } + } + + if (noChildNews) { + mailrcFile = files[0]; + } else { + String childNewsPathName = files[0].getAbsolutePath() + + System.getProperty("file.separator") + "News"; + mailrcFile = new File(childNewsPathName); + } + + } + + // return a File representing the News dir in a profile + return mailrcFile; + } +} + + + +class DirFilter implements FileFilter { + public boolean accept(File aFile) { + return aFile.isDirectory(); + } +} + + +class VersionFilter implements FileFilter { + public boolean accept(File aFile) { + if (System.getProperty("os.name").indexOf("Windows") != -1) { + if (aFile.getName().compareToIgnoreCase("News") == 0 || + aFile.getName().indexOf(".rc") != -1) { + return true; + } + } else { + if (aFile.getName().compareToIgnoreCase("News") == 0 || + aFile.getName().indexOf("newsrc") != -1) { + return true; + } + } + + return false; + } +} diff --git a/scripting/examples/java/debugger/DebugRunner.java b/scripting/examples/java/debugger/DebugRunner.java new file mode 100644 index 000000000..a3b4869fe --- /dev/null +++ b/scripting/examples/java/debugger/DebugRunner.java @@ -0,0 +1,87 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; +import java.net.URLDecoder; + +import com.sun.star.uno.XComponentContext; +import com.sun.star.script.framework.provider.PathUtils; +import com.sun.star.script.provider.XScriptContext; + +public class DebugRunner { + + private static final String FILE_URL_PREFIX = + System.getProperty("os.name").startsWith("Windows") == true ? + "file:///" : "file://"; + + public void go(final XScriptContext xsctxt, String language, String uri, + String filename) { + + OOScriptDebugger debugger; + String path = ""; + + if (language.equals("JavaScript")) { + debugger = new OORhinoDebugger(); + } else if (language.equals("BeanShell")) { + debugger = new OOBeanShellDebugger(); + } else { + return; + } + + if (uri.startsWith(FILE_URL_PREFIX)) { + uri = URLDecoder.decode(uri); + String s = uri.substring(FILE_URL_PREFIX.length()); + File f = new File(s); + + if (f.exists()) { + if (f.isDirectory()) { + if (!filename.equals("")) { + path = new File(f, filename).getAbsolutePath(); + } + } else { + path = f.getAbsolutePath(); + } + } + + debugger.go(xsctxt, path); + } else { + if (!uri.endsWith("/")) { + uri += "/"; + } + + String script = uri + filename; + InputStream is; + + try { + is = PathUtils.getScriptFileStream( + script, xsctxt.getComponentContext()); + + if (is != null) { + debugger.go(xsctxt, is); + } + } catch (IOException ioe) { + System.out.println("Error loading script: " + script); + } + } + } +} diff --git a/scripting/examples/java/debugger/OOBeanShellDebugger.java b/scripting/examples/java/debugger/OOBeanShellDebugger.java new file mode 100644 index 000000000..68a508784 --- /dev/null +++ b/scripting/examples/java/debugger/OOBeanShellDebugger.java @@ -0,0 +1,393 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import javax.swing.JFrame; +import javax.swing.JTextArea; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.text.Document; +import javax.swing.event.DocumentListener; +import javax.swing.event.DocumentEvent; + +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Dimension; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import java.io.File; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import com.sun.star.script.provider.XScriptContext; +import bsh.Interpreter; + +public class OOBeanShellDebugger implements OOScriptDebugger, ActionListener, + DocumentListener { + + private JFrame frame; + private JTextArea ta; + private GlyphGutter gg; + private XScriptContext context; + private int currentPosition = -1; + private int linecount; + private Interpreter sessionInterpreter; + private Thread execThread = null; + private String filename = null; + + /* Entry point for script execution */ + public void go(XScriptContext context, String filename) { + if (filename != null && filename != "") { + try { + FileInputStream fis = new FileInputStream(filename); + this.filename = filename; + go(context, fis); + } catch (IOException ioe) { + JOptionPane.showMessageDialog(frame, + "Error loading file: " + ioe.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + + /* Entry point for script execution */ + public void go(XScriptContext context, InputStream in) { + this.context = context; + initUI(); + + if (in != null) { + try { + loadFile(in); + } catch (IOException ioe) { + JOptionPane.showMessageDialog(frame, + "Error loading stream: " + ioe.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + + public void loadFile(InputStream in) throws IOException { + + /* Remove ourselves as a DocumentListener while loading the file + so we don't get a storm of DocumentEvents during loading */ + ta.getDocument().removeDocumentListener(this); + + byte[] contents = new byte[1024]; + int len = 0, pos = 0; + + while ((len = in.read(contents, 0, 1024)) != -1) { + ta.insert(new String(contents, 0, len), pos); + pos += len; + } + + try { + in.close(); + } catch (IOException ignore) { + } + + /* Update the GlyphGutter and add back the DocumentListener */ + gg.update(); + ta.getDocument().addDocumentListener(this); + } + + private void initUI() { + frame = new JFrame("BeanShell Debug Window"); + ta = new JTextArea(); + ta.setRows(15); + ta.setColumns(40); + ta.setLineWrap(false); + linecount = ta.getLineCount(); + + gg = new GlyphGutter(this); + + final JScrollPane sp = new JScrollPane(); + sp.setViewportView(ta); + sp.setRowHeaderView(gg); + + ta.getDocument().addDocumentListener(this); + String[] labels = {"Run", "Clear", "Save", "Close"}; + JPanel p = new JPanel(); + p.setLayout(new FlowLayout()); + + for (int i = 0; i < labels.length; i++) { + JButton b = new JButton(labels[i]); + b.addActionListener(this); + p.add(b); + + if (labels[i].equals("Save") && filename == null) { + b.setEnabled(false); + } + } + + frame.getContentPane().add(sp, "Center"); + frame.getContentPane().add(p, "South"); + frame.pack(); + frame.show(); + } + + /* Implementation of DocumentListener interface */ + public void insertUpdate(DocumentEvent e) { + doChanged(e); + } + + public void removeUpdate(DocumentEvent e) { + doChanged(e); + } + + public void changedUpdate(DocumentEvent e) { + doChanged(e); + } + + /* If the number of lines in the JTextArea has changed then update the + GlyphGutter */ + public void doChanged(DocumentEvent e) { + if (linecount != ta.getLineCount()) { + gg.update(); + linecount = ta.getLineCount(); + } + } + + private void startExecution() { + execThread = new Thread() { + public void run() { + Interpreter interpreter = new Interpreter(); + interpreter.getNameSpace().clear(); + + // reset position and repaint gutter so no red arrow appears + currentPosition = -1; + gg.repaint(); + + try { + interpreter.set("context", context); + interpreter.eval(ta.getText()); + } catch (bsh.EvalError err) { + currentPosition = err.getErrorLineNumber() - 1; + + try { + // scroll to line of the error + int line = ta.getLineStartOffset(currentPosition); + Rectangle rect = ta.modelToView(line); + ta.scrollRectToVisible(rect); + } catch (Exception e) { + // couldn't scroll to line, do nothing + } + + gg.repaint(); + + JOptionPane.showMessageDialog(frame, "Error at line " + + String.valueOf(err.getErrorLineNumber()) + + "\n\n: " + err.getErrorText(), + "Error", JOptionPane.ERROR_MESSAGE); + } catch (Exception e) { + JOptionPane.showMessageDialog(frame, + "Error: " + e.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + } + } + }; + execThread.start(); + } + + private void promptForSaveName() { + JFileChooser chooser = new JFileChooser(); + chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { + public boolean accept(File f) { + if (f.isDirectory() || f.getName().endsWith(".bsh")) { + return true; + } + + return false; + } + + public String getDescription() { + return ("BeanShell files: *.bsh"); + } + }); + + int ret = chooser.showSaveDialog(frame); + + if (ret == JFileChooser.APPROVE_OPTION) { + filename = chooser.getSelectedFile().getAbsolutePath(); + + if (!filename.endsWith(".bsh")) { + filename += ".bsh"; + } + } + + } + + private void saveTextArea() { + if (filename == null) { + promptForSaveName(); + } + + FileOutputStream fos = null; + + if (filename != null) { + try { + File f = new File(filename); + fos = new FileOutputStream(f); + String s = ta.getText(); + fos.write(s.getBytes(), 0, s.length()); + } catch (IOException ioe) { + JOptionPane.showMessageDialog(frame, + "Error saving file: " + ioe.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException ignore) { + } + } + } + } + } + + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("Run")) { + startExecution(); + } else if (e.getActionCommand().equals("Close")) { + frame.dispose(); + } else if (e.getActionCommand().equals("Save")) { + saveTextArea(); + } else if (e.getActionCommand().equals("Clear")) { + ta.setText(""); + } + } + + public JTextArea getTextArea() { + return ta; + } + + public int getCurrentPosition() { + return currentPosition; + } +} + +class GlyphGutter extends JComponent { + + private OOBeanShellDebugger debugger; + private final String DUMMY_STRING = "99"; + + GlyphGutter(OOBeanShellDebugger debugger) { + this.debugger = debugger; + update(); + } + + public void update() { + JTextArea textArea = debugger.getTextArea(); + Font font = textArea.getFont(); + setFont(font); + + FontMetrics metrics = getFontMetrics(font); + int h = metrics.getHeight(); + int lineCount = textArea.getLineCount() + 1; + + String dummy = Integer.toString(lineCount); + + if (dummy.length() < 2) { + dummy = DUMMY_STRING; + } + + Dimension d = new Dimension(); + d.width = metrics.stringWidth(dummy) + 16; + d.height = lineCount * h + 100; + setPreferredSize(d); + setSize(d); + } + + public void paintComponent(Graphics g) { + JTextArea textArea = debugger.getTextArea(); + + Font font = textArea.getFont(); + g.setFont(font); + + FontMetrics metrics = getFontMetrics(font); + Rectangle clip = g.getClipBounds(); + + g.setColor(getBackground()); + g.fillRect(clip.x, clip.y, clip.width, clip.height); + + int ascent = metrics.getMaxAscent(); + int h = metrics.getHeight(); + int lineCount = textArea.getLineCount() + 1; + + int startLine = clip.y / h; + int endLine = (clip.y + clip.height) / h + 1; + int width = getWidth(); + + if (endLine > lineCount) { + endLine = lineCount; + } + + for (int i = startLine; i < endLine; i++) { + String text; + text = Integer.toString(i + 1) + " "; + int w = metrics.stringWidth(text); + int y = i * h; + g.setColor(Color.blue); + g.drawString(text, 0, y + ascent); + int x = width - ascent; + + // if currentPosition is not -1 then a red arrow will be drawn + if (i == debugger.getCurrentPosition()) { + drawArrow(g, ascent, x, y); + } + } + } + + private void drawArrow(Graphics g, int ascent, int x, int y) { + Polygon arrow = new Polygon(); + int dx = x; + y += ascent - 10; + int dy = y; + arrow.addPoint(dx, dy + 3); + arrow.addPoint(dx + 5, dy + 3); + + for (x = dx + 5; x <= dx + 10; x++, y++) { + arrow.addPoint(x, y); + } + + for (x = dx + 9; x >= dx + 5; x--, y++) { + arrow.addPoint(x, y); + } + + arrow.addPoint(dx + 5, dy + 7); + arrow.addPoint(dx, dy + 7); + + g.setColor(Color.red); + g.fillPolygon(arrow); + g.setColor(Color.black); + g.drawPolygon(arrow); + } +}; + diff --git a/scripting/examples/java/debugger/OORhinoDebugger.java b/scripting/examples/java/debugger/OORhinoDebugger.java new file mode 100644 index 000000000..5144637ac --- /dev/null +++ b/scripting/examples/java/debugger/OORhinoDebugger.java @@ -0,0 +1,97 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import javax.swing.SwingUtilities; +import java.io.InputStream; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.tools.debugger.Main; +import org.mozilla.javascript.tools.debugger.ScopeProvider; + +import com.sun.star.script.provider.XScriptContext; + +public class OORhinoDebugger implements OOScriptDebugger { + + public void go(final XScriptContext xsctxt, String filename) { + Main sdb = initUI(xsctxt); + + // This is the method we've added to open a file when starting + // the Rhino debugger + sdb.openFile(filename); + } + + public void go(final XScriptContext xsctxt, InputStream in) { + Main sdb = initUI(xsctxt); + + // Open a stream in the debugger + sdb.openStream(in); + } + + // This code is based on the main method of the Rhino Debugger Main class + // We pass in the XScriptContext in the global scope for script execution + private Main initUI(final XScriptContext xsctxt) { + try { + final Main sdb = new Main("Rhino JavaScript Debugger"); + swingInvoke(new Runnable() { + public void run() { + sdb.pack(); + sdb.setSize(640, 640); + sdb.setVisible(true); + } + }); + sdb.setExitAction(new Runnable() { + public void run() { + sdb.dispose(); + } + }); + Context.addContextListener(sdb); + sdb.setScopeProvider(new ScopeProvider() { + public Scriptable getScope() { + Context ctxt = Context.enter(); + ImporterTopLevel scope = new ImporterTopLevel(ctxt); + Scriptable jsArgs = Context.toObject(xsctxt, scope); + scope.put("XSCRIPTCONTEXT", scope, jsArgs); + Context.exit(); + return scope; + } + }); + return sdb; + } catch (Exception exc) { + exc.printStackTrace(); + } + + return null; + } + + static void swingInvoke(Runnable f) { + if (SwingUtilities.isEventDispatchThread()) { + f.run(); + return; + } + + try { + SwingUtilities.invokeAndWait(f); + } catch (Exception exc) { + exc.printStackTrace(); + } + } +} diff --git a/scripting/examples/java/debugger/OOScriptDebugger.java b/scripting/examples/java/debugger/OOScriptDebugger.java new file mode 100644 index 000000000..61fdbe73d --- /dev/null +++ b/scripting/examples/java/debugger/OOScriptDebugger.java @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import java.io.InputStream; +import com.sun.star.script.provider.XScriptContext; + +public interface OOScriptDebugger { + public void go(XScriptContext ctxt, String filename); + public void go(XScriptContext ctxt, InputStream in); +} diff --git a/scripting/examples/java/debugger/parcel-descriptor.xml b/scripting/examples/java/debugger/parcel-descriptor.xml new file mode 100644 index 000000000..93fbeabbe --- /dev/null +++ b/scripting/examples/java/debugger/parcel-descriptor.xml @@ -0,0 +1,34 @@ +<?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 . +--> +<parcel language="Java"> + + <script language="Java"> + <locale lang="en"> + <displayname value="Scripting Framework Debugger" /> + <description>Script that starts debuggers for JavaScript and BeanShell + </description> + </locale> + <logicalname value="org.libreoffice.example.java_scripts._$DebugRunner.Debug" /> + <functionname value="DebugRunner.go" /> + <languagedepprops> + <prop name="classpath" value="debugger.jar"/> + </languagedepprops> + </script> +</parcel> + diff --git a/scripting/examples/java/selector/ScriptSelector.java b/scripting/examples/java/selector/ScriptSelector.java new file mode 100644 index 000000000..ed3cfd441 --- /dev/null +++ b/scripting/examples/java/selector/ScriptSelector.java @@ -0,0 +1,488 @@ +/* + * 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 . + */ + +package org.libreoffice.example.java_scripts; + +import javax.swing.*; +import javax.swing.tree.*; +import javax.swing.table.*; +import javax.swing.event.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.beans.*; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.Exception; +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.XComponentContext; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XComponent; +import com.sun.star.frame.XModel; +import com.sun.star.frame.FrameSearchFlag; +import com.sun.star.frame.XDispatchProvider; +import com.sun.star.frame.XDispatchHelper; +import com.sun.star.frame.XDispatch; +import com.sun.star.util.XURLTransformer; +import com.sun.star.beans.*; +import com.sun.star.script.XInvocation; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNodeFactory; +import com.sun.star.script.browse.BrowseNodeFactoryViewTypes; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.script.provider.XScript; +import com.sun.star.script.provider.XScriptProvider; + +public class ScriptSelector { + + private static final int BIG_GAP = 10; + private static final int MED_GAP = 5; + + private ScriptSelectorPanel selectorPanel; + + public ScriptSelector() { + } + + public void showOrganizer(final XScriptContext ctxt) { + try { + XBrowseNode root = getRootNode(ctxt); + + final XScriptProvider msp = + (XScriptProvider)UnoRuntime.queryInterface( + XScriptProvider.class, root); + + final JFrame client = new JFrame("Script"); + + selectorPanel = new ScriptSelectorPanel(root); + + final JButton runButton, closeButton, createButton, + editButton, deleteButton; + + runButton = new JButton("Run"); + runButton.setEnabled(false); + + closeButton = new JButton("Close"); + + editButton = new JButton("Edit"); + editButton.setEnabled(false); + + JPanel northButtons = + new JPanel(new GridLayout(2, 1, MED_GAP, MED_GAP)); + + northButtons.add(runButton); + northButtons.add(closeButton); + + createButton = new JButton("Create"); + createButton.setEnabled(false); + + deleteButton = new JButton("Delete"); + deleteButton.setEnabled(false); + + JPanel southButtons = + new JPanel(new GridLayout(3, 1, MED_GAP, MED_GAP)); + + southButtons.add(editButton); + southButtons.add(createButton); + southButtons.add(deleteButton); + + selectorPanel.tree.addTreeSelectionListener( + new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent e) { + XBrowseNode xbn = selectorPanel.getSelection(); + XPropertySet props = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class, xbn); + + checkEnabled(props, "Creatable", createButton); + checkEnabled(props, "Deletable", deleteButton); + checkEnabled(props, "Editable", editButton); + + if (xbn != null && + xbn.getType() == BrowseNodeTypes.SCRIPT) { + runButton.setEnabled(true); + } else { + runButton.setEnabled(false); + } + } + } + ); + + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent event) { + if (event.getSource() == runButton) { + String uri = selectorPanel.textField.getText(); + + try { + XScript script = msp.getScript(uri); + + Object[][] out = new Object[1][0]; + out[0] = new Object[0]; + + short[][] num = new short[1][0]; + num[0] = new short[0]; + + script.invoke(new Object[0], num, out); + } catch (Exception e) { + e.printStackTrace(); + } + } else if (event.getSource() == closeButton) { + client.dispose(); + } else if (event.getSource() == editButton) { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode) + selectorPanel.tree.getLastSelectedPathComponent(); + + if (node == null) return; + + showEditor(ctxt, node); + } else if (event.getSource() == createButton) { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode) + selectorPanel.tree.getLastSelectedPathComponent(); + + if (node == null) return; + + doCreate(ctxt, node); + } else if (event.getSource() == deleteButton) { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode) + selectorPanel.tree.getLastSelectedPathComponent(); + + if (node == null) return; + + doDelete(ctxt, node); + } + } + }; + + runButton.addActionListener(listener); + closeButton.addActionListener(listener); + createButton.addActionListener(listener); + editButton.addActionListener(listener); + deleteButton.addActionListener(listener); + + JPanel buttonPanel = new JPanel(new BorderLayout()); + buttonPanel.add(northButtons, BorderLayout.NORTH); + buttonPanel.add(southButtons, BorderLayout.SOUTH); + + JPanel mainPanel = new JPanel(new BorderLayout(MED_GAP, MED_GAP)); + mainPanel.setBorder( + new EmptyBorder(BIG_GAP, BIG_GAP, BIG_GAP, BIG_GAP)); + mainPanel.add(selectorPanel, BorderLayout.CENTER); + mainPanel.add(buttonPanel, BorderLayout.EAST); + + client.getContentPane().add(mainPanel); + client.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + client.setSize(500, 350); + + // set the x and y locations so that the frame is in the + // centre of the screen + Dimension d = client.getToolkit().getScreenSize(); + + int x = (int)((d.getWidth() - client.getWidth()) / 2); + int y = (int)((d.getHeight() - client.getHeight()) / 2); + + client.setLocation(x, y); + + client.show(); + } catch (com.sun.star.uno.RuntimeException rue) { + rue.printStackTrace(); + } catch (java.lang.Exception e) { + e.printStackTrace(); + } + } + + public void showOrganizer(final XScriptContext ctxt, + final com.sun.star.awt.MouseEvent e) { + showOrganizer(ctxt); + } + + public void showOrganizer(final XScriptContext ctxt, + final com.sun.star.awt.ActionEvent e) { + showOrganizer(ctxt); + } + + private void doDelete( + XScriptContext ctxt, DefaultMutableTreeNode node) { + Object obj = node.getUserObject(); + XInvocation inv = + (XInvocation)UnoRuntime.queryInterface( + XInvocation.class, obj); + Object[] args = new Object[] { ctxt }; + + try { + Object result = inv.invoke("Deletable", args, + new short[1][0], new Object[1][0]); + + if (result != null && AnyConverter.toBoolean(result) == true) { + selectorPanel.removeNode(node); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void doCreate( + XScriptContext ctxt, DefaultMutableTreeNode node) { + Object obj = node.getUserObject(); + XInvocation inv = + (XInvocation)UnoRuntime.queryInterface( + XInvocation.class, obj); + Object[] args = new Object[] { ctxt }; + + try { + Object result = inv.invoke("Creatable", args, + new short[1][0], new Object[1][0]); + + if (result != null) { + XBrowseNode xbn = (XBrowseNode) + AnyConverter.toObject(new Type(XBrowseNode.class), result); + selectorPanel.addNode(node, xbn); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void showEditor( + XScriptContext ctxt, DefaultMutableTreeNode node) { + Object obj = node.getUserObject(); + XInvocation inv = + (XInvocation)UnoRuntime.queryInterface( + XInvocation.class, obj); + Object[] args = new Object[] { ctxt }; + + try { + inv.invoke("Editable", args, + new short[1][0], new Object[1][0]); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void checkEnabled(XPropertySet props, String name, + JButton button) { + boolean enable = false; + + try { + if (props != null) { + Object o = props.getPropertyValue(name); + enable = AnyConverter.toBoolean( + props.getPropertyValue(name)); + } + } catch (com.sun.star.lang.IllegalArgumentException iae) { + // leave enable set to false + } catch (com.sun.star.beans.UnknownPropertyException upe) { + // leave enable set to false + } catch (com.sun.star.lang.WrappedTargetException wte) { + // leave enable set to false + } + + button.setEnabled(enable); + } + + private XBrowseNode getRootNode(XScriptContext ctxt) { + + XBrowseNode result = null; + + + XComponentContext xcc = ctxt.getComponentContext(); + XMultiComponentFactory xmcf = xcc.getServiceManager(); + XBrowseNodeFactory xBrowseFac = (XBrowseNodeFactory) + UnoRuntime.queryInterface(XBrowseNodeFactory.class, xcc.getValueByName( + "/singletons/com.sun.star.script.browse.theBrowseNodeFactory")); + + + result = (XBrowseNode)UnoRuntime.queryInterface( + XBrowseNode.class, xBrowseFac.createView( + BrowseNodeFactoryViewTypes.MACROORGANIZER)); + return result; + } +} + +class ScriptSelectorPanel extends JPanel { + + private XBrowseNode myrootnode = null; + public JTextField textField; + public JTree tree; + public DefaultTreeModel treeModel; + + public ScriptSelectorPanel(XBrowseNode root) { + this.myrootnode = root; + initUI(); + } + + public XBrowseNode getSelection() { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) + tree.getLastSelectedPathComponent(); + + if (node == null) { + return null; + } + + return (XBrowseNode)node.getUserObject(); + } + + private void initUI() { + setLayout(new BorderLayout()); + + DefaultMutableTreeNode top = + new DefaultMutableTreeNode(myrootnode) { + public String toString() { + return ((XBrowseNode)getUserObject()).getName(); + } + }; + initNodes(myrootnode, top); + treeModel = new DefaultTreeModel(top); + tree = new JTree(treeModel); + + tree.setCellRenderer(new ScriptTreeRenderer()); + + tree.getSelectionModel().setSelectionMode + (TreeSelectionModel.SINGLE_TREE_SELECTION); + + tree.addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent e) { + XBrowseNode xbn = getSelection(); + XPropertySet props = (XPropertySet)UnoRuntime.queryInterface( + XPropertySet.class, xbn); + + if (xbn == null) { + textField.setText(""); + return; + } + + String str = xbn.getName(); + + if (xbn.getType() == BrowseNodeTypes.SCRIPT && props != null) { + try { + str = AnyConverter.toString( + props.getPropertyValue("URI")); + } catch (Exception ignore) { + // default will be used + } + } + + textField.setText(str); + } + }); + + JScrollPane scroller = new JScrollPane(tree); + add(scroller, BorderLayout.CENTER); + + textField = new JTextField(); + add(textField, BorderLayout.SOUTH); + } + + public void removeNode(DefaultMutableTreeNode node) { + MutableTreeNode parent = (MutableTreeNode)(node.getParent()); + + if (parent != null) { + treeModel.removeNodeFromParent(node); + } + } + + public void addNode(DefaultMutableTreeNode parent, XBrowseNode xbn) { + DefaultMutableTreeNode newNode = + new DefaultMutableTreeNode(xbn) { + public String toString() { + return ((XBrowseNode)getUserObject()).getName(); + } + }; + + treeModel.insertNodeInto(newNode, parent, parent.getChildCount()); + tree.scrollPathToVisible(new TreePath(newNode.getPath())); + } + + private void initNodes(XBrowseNode parent, DefaultMutableTreeNode top) { + if (parent == null || parent.hasChildNodes() == false) { + return; + } + + XBrowseNode[] children = parent.getChildNodes(); + + try { + if (children != null) { + for (int i = 0; i < children.length; i++) { + if (children[i] == null) { + continue; + } + + DefaultMutableTreeNode newNode = + new DefaultMutableTreeNode(children[i]) { + public String toString() { + return ((XBrowseNode)getUserObject()).getName(); + } + }; + top.add(newNode); + initNodes(children[i], newNode); + } + } + } catch (java.lang.Exception e) { + e.printStackTrace(); + } + } +} + +class ScriptTreeRenderer extends DefaultTreeCellRenderer { + + private ImageIcon sofficeIcon; + private ImageIcon scriptIcon; + private ImageIcon containerIcon; + + public ScriptTreeRenderer() { + sofficeIcon = new ImageIcon(getClass().getResource("soffice.gif")); + scriptIcon = new ImageIcon(getClass().getResource("script.gif")); + containerIcon = new ImageIcon(getClass().getResource("container.gif")); + } + + public Component getTreeCellRendererComponent( + JTree tree, + Object value, + boolean sel, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + + super.getTreeCellRendererComponent( + tree, value, sel, + expanded, leaf, row, + hasFocus); + + DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; + XBrowseNode xbn = (XBrowseNode)node.getUserObject(); + + if (xbn.getType() == BrowseNodeTypes.SCRIPT) { + setIcon(scriptIcon); + } else if (xbn.getType() == BrowseNodeTypes.CONTAINER) { + setIcon(containerIcon); + } else if (xbn.getType() == BrowseNodeTypes.ROOT) { + setIcon(sofficeIcon); + } + + return this; + } +} diff --git a/scripting/examples/java/selector/container.gif b/scripting/examples/java/selector/container.gif Binary files differnew file mode 100644 index 000000000..3a345f9bf --- /dev/null +++ b/scripting/examples/java/selector/container.gif diff --git a/scripting/examples/java/selector/parcel-descriptor.xml b/scripting/examples/java/selector/parcel-descriptor.xml new file mode 100644 index 000000000..1f4b8ec38 --- /dev/null +++ b/scripting/examples/java/selector/parcel-descriptor.xml @@ -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 . +--> +<parcel language="Java" xmlns:parcel="scripting.dtd"> + <script language="Java"> + <locale lang="en"> + <displayname value="Script Selector"/> + <description> + Prototype Script Selector GUI for StarOffice + </description> + </locale> + <functionname value="org.libreoffice.example.java_scripts.ScriptSelector.showOrganizer"/> + <logicalname value="ScriptSelector.showOrganizer"/> + <languagedepprops> + <prop name="classpath" value="selector.jar"/> + </languagedepprops> + </script> +</parcel> diff --git a/scripting/examples/java/selector/script.gif b/scripting/examples/java/selector/script.gif Binary files differnew file mode 100644 index 000000000..d3b3768ca --- /dev/null +++ b/scripting/examples/java/selector/script.gif diff --git a/scripting/examples/java/selector/soffice.gif b/scripting/examples/java/selector/soffice.gif Binary files differnew file mode 100644 index 000000000..88124d87d --- /dev/null +++ b/scripting/examples/java/selector/soffice.gif diff --git a/scripting/examples/javascript/ExportSheetsToHTML/exportsheetstohtml.js b/scripting/examples/javascript/ExportSheetsToHTML/exportsheetstohtml.js new file mode 100644 index 000000000..37f9e5afb --- /dev/null +++ b/scripting/examples/javascript/ExportSheetsToHTML/exportsheetstohtml.js @@ -0,0 +1,88 @@ +/* + * 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 . + */ +// When this script is run on an existing, saved, spreadsheet, +// eg. /home/testuser/myspreadsheet.sxc, the script will export +// each sheet to a separate html file, +// eg. /home/testuser/myspreadsheet_sheet1.html, +// /home/testuser/myspreadsheet_sheet2.html etc +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.sheet.XSpreadsheetDocument); +importClass(Packages.com.sun.star.container.XIndexAccess); +importClass(Packages.com.sun.star.beans.XPropertySet); +importClass(Packages.com.sun.star.beans.PropertyValue); +importClass(Packages.com.sun.star.util.XModifiable); +importClass(Packages.com.sun.star.frame.XStorable); +importClass(Packages.com.sun.star.frame.XModel); +importClass(Packages.com.sun.star.uno.AnyConverter); +importClass(Packages.com.sun.star.uno.Type); + +importClass(java.lang.System); + +//get the document object from the scripting context +oDoc = XSCRIPTCONTEXT.getDocument(); +//get the XSpreadsheetDocument interface from the document +xSDoc = UnoRuntime.queryInterface(XSpreadsheetDocument, oDoc); +//get the XModel interface from the document +xModel = UnoRuntime.queryInterface(XModel,oDoc); +//get the XIndexAccess interface used to access each sheet +xSheetsIndexAccess = UnoRuntime.queryInterface(XIndexAccess, xSDoc.getSheets()); +//get the XStorable interface used to save the document +xStorable = UnoRuntime.queryInterface(XStorable,xSDoc); +//get the XModifiable interface used to indicate if the document has been +//changed +xModifiable = UnoRuntime.queryInterface(XModifiable,xSDoc); + +//set up an array of PropertyValue objects used to save each sheet in the +//document +storeProps = new Array;//PropertyValue[1]; +storeProps[0] = new PropertyValue(); +storeProps[0].Name = "FilterName"; +storeProps[0].Value = "HTML (StarCalc)"; +storeUrl = xModel.getURL(); +storeUrl = storeUrl.substring(0,storeUrl.lastIndexOf('.')); + +//set only one sheet visible, and store to HTML doc +for(var i=0;i<xSheetsIndexAccess.getCount();i++) +{ + setAllButOneHidden(xSheetsIndexAccess,i); + xModifiable.setModified(false); + xStorable.storeToURL(storeUrl+"_sheet"+(i+1)+".html", storeProps); +} + +// now set all visible again +for(var i=0;i<xSheetsIndexAccess.getCount();i++) +{ + xPropSet = AnyConverter.toObject( new Type(XPropertySet), xSheetsIndexAccess.getByIndex(i)); + xPropSet.setPropertyValue("IsVisible", true); +} + +function setAllButOneHidden(xSheetsIndexAccess,vis) { + //System.err.println("count="+xSheetsIndexAccess.getCount()); + //get an XPropertySet interface for the vis-th sheet + xPropSet = AnyConverter.toObject( new Type(XPropertySet), xSheetsIndexAccess.getByIndex(vis)); + //set the vis-th sheet to be visible + xPropSet.setPropertyValue("IsVisible", true); + // set all other sheets to be invisible + for(var i=0;i<xSheetsIndexAccess.getCount();i++) + { + xPropSet = AnyConverter.toObject( new Type(XPropertySet), xSheetsIndexAccess.getByIndex(i)); + if(i!=vis) { + xPropSet.setPropertyValue("IsVisible", false); + } + } +} diff --git a/scripting/examples/javascript/ExportSheetsToHTML/parcel-descriptor.xml b/scripting/examples/javascript/ExportSheetsToHTML/parcel-descriptor.xml new file mode 100644 index 000000000..be8880ab0 --- /dev/null +++ b/scripting/examples/javascript/ExportSheetsToHTML/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="JavaScript" xmlns:parcel="scripting.dtd"> + + <script language="JavaScript"> + <locale lang="en"> + <displayname value="ExportSheetsToHTML"/> + <description> + Saves each sheet in the current Calc document as a separate HTML file in the same directory as the original Calc document. + </description> + </locale> + <functionname value="exportsheetstohtml.js"/> + <logicalname value="ExportSheetsToHTML.JavaScript"/> + </script> + +</parcel> diff --git a/scripting/examples/javascript/HelloWorld/helloworld.js b/scripting/examples/javascript/HelloWorld/helloworld.js new file mode 100644 index 000000000..f04fdfd8a --- /dev/null +++ b/scripting/examples/javascript/HelloWorld/helloworld.js @@ -0,0 +1,33 @@ +/* + * 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 . + */ +// Hello World in JavaScript +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.text.XTextDocument); +importClass(Packages.com.sun.star.text.XText); +importClass(Packages.com.sun.star.text.XTextRange); + +//get the document from the scripting context +oDoc = XSCRIPTCONTEXT.getDocument(); +//get the XTextDocument interface +xTextDoc = UnoRuntime.queryInterface(XTextDocument,oDoc); +//get the XText interface +xText = xTextDoc.getText(); +//get an (empty) XTextRange interface at the end of the text +xTextRange = xText.getEnd(); +//set the text in the XTextRange +xTextRange.setString( "Hello World (in JavaScript)" ); diff --git a/scripting/examples/javascript/HelloWorld/parcel-descriptor.xml b/scripting/examples/javascript/HelloWorld/parcel-descriptor.xml new file mode 100644 index 000000000..65ea9fd75 --- /dev/null +++ b/scripting/examples/javascript/HelloWorld/parcel-descriptor.xml @@ -0,0 +1,32 @@ +<?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 . +--> +<parcel language="JavaScript" xmlns:parcel="scripting.dtd"> + + <script language="JavaScript"> + <locale lang="en"> + <displayname value="Hello World"/> + <description> + Adds the string "Hello World" into the current text doc. + </description> + </locale> + <functionname value="helloworld.js"/> + <logicalname value="HelloWorld.JavaScript"/> + </script> + +</parcel> diff --git a/scripting/examples/javascript/Highlight/ButtonPressHandler.js b/scripting/examples/javascript/Highlight/ButtonPressHandler.js new file mode 100644 index 000000000..e0fbde240 --- /dev/null +++ b/scripting/examples/javascript/Highlight/ButtonPressHandler.js @@ -0,0 +1,122 @@ +/* + * 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 . + */ +//this script acts as a handler for the buttons in the Highlight dialog +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.uno.Type); +importClass(Packages.com.sun.star.uno.AnyConverter); + +importClass(Packages.com.sun.star.awt.XButton); +importClass(Packages.com.sun.star.awt.XControl); +importClass(Packages.com.sun.star.awt.ActionEvent); +importClass(Packages.com.sun.star.awt.XControlModel); +importClass(Packages.com.sun.star.awt.XControlContainer); +importClass(Packages.com.sun.star.awt.XDialog); +importClass(Packages.com.sun.star.awt.XTextComponent); + +importClass(Packages.com.sun.star.util.XReplaceable); +importClass(Packages.com.sun.star.util.XReplaceDescriptor); +importClass(Packages.com.sun.star.util.XPropertyReplace); + +importClass(Packages.com.sun.star.beans.XPropertySet); +importClass(Packages.com.sun.star.beans.PropertyValue); + +// Scripting Framework DialogFactory class +importClass(Packages.com.sun.star.script.framework.browse.DialogFactory); + +// Get the ActionEvent object from the ARGUMENTS list +event = ARGUMENTS[0]; + +// Each argument is of type Any so we must use the AnyConverter class to +// convert it into the interface or primitive type we expect +button = AnyConverter.toObject(new Type(XButton), event.Source); + +// We can now query for the model of the button and get its properties +control = UnoRuntime.queryInterface(XControl, button); +cmodel = control.getModel(); +pset = UnoRuntime.queryInterface(XPropertySet, cmodel); + +if (pset.getPropertyValue("Label").equals("Exit")) +{ + // We can get the XDialog in which this control appears by calling + // getContext() on the XControl interface + xDialog = UnoRuntime.queryInterface( + XDialog, control.getContext()); + + // Close the dialog + xDialog.endExecute(); +} +else +{ + // We can get the list of controls for this dialog by calling + // getContext() on the XControl interface of the button + controls = UnoRuntime.queryInterface( + XControlContainer, control.getContext()); + + // Now get the text field control from the list + textField = + UnoRuntime.queryInterface( + XTextComponent, controls.getControl("HighlightTextField")); + + searchKey = textField.getText(); + + // highlight the text in red + red = java.awt.Color.red.getRGB(); + + replaceable = + UnoRuntime.queryInterface(XReplaceable, XSCRIPTCONTEXT.getDocument()); + + descriptor = replaceable.createReplaceDescriptor(); + + // Gets a XPropertyReplace object for altering the properties + // of the replaced text + xPropertyReplace = UnoRuntime.queryInterface(XPropertyReplace, descriptor); + + // Sets the replaced text property fontweight value to Bold + wv = new PropertyValue("CharWeight", -1, + new java.lang.Float(Packages.com.sun.star.awt.FontWeight.BOLD), + Packages.com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Sets the replaced text property color value to RGB parameter + cv = new PropertyValue("CharColor", -1, + new java.lang.Integer(red), + Packages.com.sun.star.beans.PropertyState.DIRECT_VALUE); + + // Apply the properties + props = new Array; + props[0] = cv; + props[1] = wv; + + try { + xPropertyReplace.setReplaceAttributes(props); + + // Only matches whole words and case sensitive + descriptor.setPropertyValue( + "SearchCaseSensitive", new java.lang.Boolean(true)); + descriptor.setPropertyValue("SearchWords", new java.lang.Boolean(true)); + + // Replaces all instances of searchKey with new Text properties + // and gets the number of instances of the searchKey + descriptor.setSearchString(searchKey); + descriptor.setReplaceString(searchKey); + replaceable.replaceAll(descriptor); + } + catch (e) { + java.lang.System.err.println("Error setting up search properties" + + e.getMessage()); + } +} diff --git a/scripting/examples/javascript/Highlight/ShowDialog.js b/scripting/examples/javascript/Highlight/ShowDialog.js new file mode 100644 index 000000000..d659d9622 --- /dev/null +++ b/scripting/examples/javascript/Highlight/ShowDialog.js @@ -0,0 +1,131 @@ +/* + * 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 . + */ +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.lang.XMultiComponentFactory); +importClass(Packages.com.sun.star.awt.XDialogProvider); +importClass(Packages.com.sun.star.awt.XDialog); +importClass(Packages.com.sun.star.uno.Exception); +importClass(Packages.com.sun.star.script.provider.XScriptContext); + +importClass(java.lang.Thread); +importClass(java.lang.System); + +function tryLoadingLibrary( xmcf, context, name ) +{ + try + { + obj = xmcf.createInstanceWithContext( + "com.sun.star.script.Application" + name + "LibraryContainer", + context.getComponentContext()); + + xLibraryContainer = UnoRuntime.queryInterface(XLibraryContainer, obj); + + System.err.println("Got XLibraryContainer"); + + serviceObj = context.getComponentContext().getValueByName( + "/singletons/com.sun.star.util.theMacroExpander"); + + xme = AnyConverter.toObject(new Type(XMacroExpander), serviceObj); + + bootstrapName = "bootstraprc"; + if (System.getProperty("os.name").startsWith("Windows")) + { + bootstrapName = "bootstrap.ini"; + } + + libURL = xme.expandMacros( + "$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/basic/ScriptBindingLibrary/" + + name.toLowerCase() + ".xlb/"); + + System.err.println("libURL is: " + libURL); + + xLibraryContainer.createLibraryLink( + "ScriptBindingLibrary", libURL, false); + + System.err.println("liblink created"); + + } + catch (e) + { + System.err.println("Got an exception loading lib: " + e.getMessage()); + return false; + } + return true; +} + +function getDialogProvider() +{ + // UNO awt components of the Highlight dialog + //get the XMultiServiceFactory + xmcf = XSCRIPTCONTEXT.getComponentContext().getServiceManager(); + + args = new Array; + //get the XDocument from the context + args[0] = XSCRIPTCONTEXT.getDocument(); + + //try to create the DialogProvider + try { + obj = xmcf.createInstanceWithArgumentsAndContext( + "com.sun.star.awt.DialogProvider", args, + XSCRIPTCONTEXT.getComponentContext()); + } + catch (e) { + System.err.println("Error getting DialogProvider object"); + return null; + } + + return UnoRuntime.queryInterface(XDialogProvider, obj); +} + +//get the DialogProvider +xDialogProvider = getDialogProvider(); + +if (xDialogProvider != null) +{ + //try to create the Highlight dialog (found in the ScriptBinding library) + try + { + findDialog = xDialogProvider.createDialog("vnd.sun.star.script:" + + "ScriptBindingLibrary.Highlight?location=application"); + if( findDialog == null ) + { + if (tryLoadingLibrary(xmcf, XSCRIPTCONTEXT, "Dialog") == false || + tryLoadingLibrary(xmcf, XSCRIPTCONTEXT, "Script") == false) + { + System.err.println("Error loading ScriptBindingLibrary"); + } + else + { + // try to create the Highlight dialog (found in the + // ScriptBindingLibrary) + findDialog = xDialogProvider.createDialog("vnd.sun.star.script:" + + "ScriptBindingLibrary.Highlight?location=application"); + } + } + + //launch the dialog + if ( findDialog != null ) + { + findDialog.execute(); + } + } + catch (e) { + System.err.println("Got exception on first creating dialog: " + + e.getMessage()); + } +} diff --git a/scripting/examples/javascript/Highlight/parcel-descriptor.xml b/scripting/examples/javascript/Highlight/parcel-descriptor.xml new file mode 100644 index 000000000..e6d6bb7b1 --- /dev/null +++ b/scripting/examples/javascript/Highlight/parcel-descriptor.xml @@ -0,0 +1,41 @@ +<?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 . +--> +<parcel language="JavaScript" xmlns:parcel="scripting.dtd"> + <script language="JavaScript"> + <locale lang="en"> + <displayname value="ShowDialog" /> + <description> + Example of how to show a dialog from JavaScript + </description> + </locale> + <functionname value="ShowDialog.js" /> + <logicalname value="ShowDialog.JavaScript" /> + </script> + <script language="JavaScript"> + <locale lang="en"> + <displayname value="ButtonPressHandler" /> + <description> + Example of handle button press events for the Dialog + </description> + </locale> + <functionname value="ButtonPressHandler.js" /> + <logicalname value="ButtonPressHandler.JavaScript" /> + </script> +</parcel> + diff --git a/scripting/examples/python/Capitalise.py b/scripting/examples/python/Capitalise.py new file mode 100644 index 000000000..64d29a513 --- /dev/null +++ b/scripting/examples/python/Capitalise.py @@ -0,0 +1,96 @@ +# +# 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 . +# + +def getNewString(theString): + """helper function + """ + if (not theString): + return "" + + # should we tokenize on "."? + if len(theString) >= 2 and theString[:2].isupper(): + # first two chars are UC => first UC, rest LC + newString = theString[0].upper() + theString[1:].lower() + + elif theString[0].isupper(): + # first char UC => all to LC + newString = theString.lower() + + else: + # all to UC. + newString = theString.upper() + + return newString + + +def capitalisePython(): + """Change the case of the selected or current word(s). + If at least the first two characters are "UPpercase, then it is changed + to first char "Uppercase". + If the first character is "Uppercase", then it is changed to + all "lowercase". + Otherwise, all are changed to "UPPERCASE". + """ + # The context variable is of type XScriptContext and is available to + # all BeanShell scripts executed by the Script Framework + xModel = XSCRIPTCONTEXT.getDocument() + + # the writer controller impl supports the css.view.XSelectionSupplier + # interface + xSelectionSupplier = xModel.getCurrentController() + + # see section 7.5.1 of developers' guide + xIndexAccess = xSelectionSupplier.getSelection() + count = xIndexAccess.getCount() + + if(count >= 1): # ie we have a selection + i = 0 + + while i < count: + xTextRange = xIndexAccess.getByIndex(i) + theString = xTextRange.getString() + # print("theString") + if len(theString) == 0: + # sadly we can have a selection where nothing is selected + # in this case we get the XWordCursor and make a selection! + xText = xTextRange.getText() + xWordCursor = xText.createTextCursorByRange(xTextRange) + + if not xWordCursor.isStartOfWord(): + xWordCursor.gotoStartOfWord(False) + + xWordCursor.gotoNextWord(True) + theString = xWordCursor.getString() + newString = getNewString(theString) + + if newString: + xWordCursor.setString(newString) + xSelectionSupplier.select(xWordCursor) + else: + newString = getNewString(theString) + if newString: + xTextRange.setString(newString) + xSelectionSupplier.select(xTextRange) + i += 1 + + +# lists the scripts, that shall be visible inside OOo. Can be omitted, if +# all functions shall be visible, however here getNewString shall be suppressed +g_exportedScripts = capitalisePython, + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/examples/python/HelloWorld.py b/scripting/examples/python/HelloWorld.py new file mode 100644 index 000000000..ed21b2008 --- /dev/null +++ b/scripting/examples/python/HelloWorld.py @@ -0,0 +1,47 @@ +# HelloWorld python script for the scripting framework + +# +# 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 . +# + +def HelloWorldPython(): + """Prints the string 'Hello World (in Python)' into the current document. + """ + + # Get the doc from the scripting context which is made available to all + # scripts. + desktop = XSCRIPTCONTEXT.getDesktop() + model = desktop.getCurrentComponent() + + # Check whether there's already an opened document. + # Otherwise, create a new one + if not hasattr(model, "Text"): + model = desktop.loadComponentFromURL( + "private:factory/swriter", "_blank", 0, ()) + + # get the XText interface + text = model.Text + + # create an XTextRange at the end of the document + tRange = text.End + + # and set the string + tRange.String = "Hello World (in Python)" + + return None + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/examples/python/InsertText.py b/scripting/examples/python/InsertText.py new file mode 100644 index 000000000..801b81908 --- /dev/null +++ b/scripting/examples/python/InsertText.py @@ -0,0 +1,65 @@ +# Example python script for the scripting framework + +# +# 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 . +# + +def InsertText(text): + """Inserts the argument string into the current document. + If there is a selection, the selection is replaced by it. + """ + + # Get the doc from the scripting context which is made available to + # all scripts. + desktop = XSCRIPTCONTEXT.getDesktop() + model = desktop.getCurrentComponent() + + # Check whether there's already an opened document. + if not hasattr(model, "Text"): + return + + # The context variable is of type XScriptContext and is available to + # all BeanShell scripts executed by the Script Framework + xModel = XSCRIPTCONTEXT.getDocument() + + # The writer controller impl supports the css.view.XSelectionSupplier + # interface. + xSelectionSupplier = xModel.getCurrentController() + + # See section 7.5.1 of developers' guide + xIndexAccess = xSelectionSupplier.getSelection() + count = xIndexAccess.getCount() + + if count >= 1: # ie we have a selection + i = 0 + + while i < count: + xTextRange = xIndexAccess.getByIndex(i) + theString = xTextRange.getString() + + if not len(theString): + # Nothing really selected, just insert. + xText = xTextRange.getText() + xWordCursor = xText.createTextCursorByRange(xTextRange) + xWordCursor.setString(text) + xSelectionSupplier.select(xWordCursor) + else: + # Replace the selection. + xTextRange.setString(text) + xSelectionSupplier.select(xTextRange) + + i += 1 diff --git a/scripting/examples/python/NamedRanges.py b/scripting/examples/python/NamedRanges.py new file mode 100644 index 000000000..5a28e2b5d --- /dev/null +++ b/scripting/examples/python/NamedRanges.py @@ -0,0 +1,134 @@ +# +# 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/. +# +import uno +from com.sun.star.container import NoSuchElementException + +def DefineNamedRange(doc, SheetName, rangeName, rangeReference): + """Defines a new named range. If the named range exists in the document, then + update the rangeReference. + + Example: DefineNamedRange(doc, "Sheet1", "test_range", '$A$1:$F$14'). + + API Reference: + https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1sheet_1_1XNamedRanges.html + """ + aName = rangeName + # make sure the sheet name starts with "$" + sheetName = "$" + SheetName.replace("$", "") + aContent = sheetName + "." + rangeReference + + try: + # If the named range exists, then update it + doc.NamedRanges.getByName(rangeName) + update = True + except NoSuchElementException: + update = False + + if update: + doc.NamedRanges.getByName(rangeName).setContent(aContent) + else: + aPosition = uno.createUnoStruct('com.sun.star.table.CellAddress') + sheet = doc.Sheets.getByName(SheetName) + # the index of the sheet in the doc, 0-based + aPosition.Sheet = sheet.getRangeAddress().Sheet + + addressObj = sheet.getCellRangeByName(rangeReference) + # (com.sun.star.table.CellRangeAddress){ Sheet = (short)0x0, StartColumn = (long)0x0, StartRow = (long)0x0, EndColumn = (long)0x5, EndRow = (long)0xd } + address = addressObj.getRangeAddress() + + aPosition.Column = address.StartColumn + aPosition.Row = address.StartRow + + doc.NamedRanges.addNewByName(aName, aContent, aPosition, 0) + + return None + +def NamedRanges(): + """The main function to be shown on the user interface.""" + ctx = uno.getComponentContext() + smgr = ctx.ServiceManager + desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) + + # Create a blank spreadsheet document, instead of damaging the existing document. + doc = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, ()) + + # Create a new sheet to store our output information + doc.Sheets.insertNewByName("Information", 1) + infoSheet = doc.Sheets.getByName("Information") + + # Set text in the information sheet + infoSheet.getCellRangeByName("A1").String = "Operation" + infoSheet.getCellRangeByName("B1").String = "Name of Cell Range" + infoSheet.getCellRangeByName("C1").String = "Content of Named Cell Range" + + # Format the information header row + infoHeaderRange = infoSheet.getCellRangeByName("A1:C1") + # 2 = CENTER, see enum CellHoriJustify in https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1table.html + infoHeaderRange.HoriJustify = 2 + infoHeaderRange.CellBackColor = 0xdee6ef + + # Defines the named range test_range1 + dataSheetName = "data" + doc.Sheets[0].Name = dataSheetName + DefineNamedRange(doc, dataSheetName, "test_range1", "$A$1:$F$14") + + # Displays the named range information + test_range1 = doc.NamedRanges.getByName("test_range1") + infoSheet.getCellRangeByName("A2").String = "Defined test_range1" + infoSheet.getCellRangeByName("B2").String = test_range1.Name + infoSheet.getCellRangeByName("C2").String = test_range1.Content + + # Revise the named ranges. + DefineNamedRange(doc, dataSheetName, "test_range1", "$A$1:$A$10") + infoSheet.getCellRangeByName("A3").String = "Revised test_range1" + infoSheet.getCellRangeByName("B3").String = test_range1.Name + infoSheet.getCellRangeByName("C3").String = test_range1.Content + + # Defines the named range test_range2 + DefineNamedRange(doc, dataSheetName, "test_range2", "$B$1:$B$10") + test_range2 = doc.NamedRanges.getByName("test_range2") + infoSheet.getCellRangeByName("A4").String = "Defined test_range2" + infoSheet.getCellRangeByName("B4").String = test_range2.Name + infoSheet.getCellRangeByName("C4").String = test_range2.Content + + # Set data to test_range1 and test_range2 + + dataSheet = doc.Sheets.getByName(dataSheetName) + # You should use a tuple for setDataArray. For range e.g. A1:E1 it should + # be in the form tuple((1,2,3,4,5)), and for range e.g. A1:A5 it should be + # in the form tuple((1,), (2,), (3,), (4,), (5,)). + data1 = tuple(((1,),(2,),(3,),(4,),(5,),(6,),(7,),(8,),(9,),(10,))) + dataSheet.getCellRangeByName(test_range1.Content).setDataArray(data1) + infoSheet.getCellRangeByName("A5").String = "Set value to test_range1" + + data2 = tuple(((2,),(4,),(6,),(8,),(10,),(12,),(14,),(16,),(18,),(20,))) + dataSheet.getCellRangeByName(test_range2.Content).setDataArray(data2) + infoSheet.getCellRangeByName("A6").String = "Set value to test_range2" + + # Calculate sum of test_range1 + infoSheet.getCellRangeByName("A8").String = "Sum of test_range1:" + infoSheet.getCellRangeByName("B8").Formula = "=SUM(test_range1)" + + # Calculate sum of test_range2 + infoSheet.getCellRangeByName("A9").String = "Sum of test_range2:" + infoSheet.getCellRangeByName("B9").Formula = "=SUM(test_range2)" + + # Calculate the difference between the two ranges + infoSheet.getCellRangeByName("A10").String = "sum(test_range2) - sum(test_range1):" + infoSheet.getCellRangeByName("B10").Formula = "=B9-B8" + + # Format the sum header columns + infoSheet.getCellRangeByName("A8:A10").CellBackColor = 0xdee6ef + + # Set column width + infoSheet.Columns.getByName("A").Width = 5590 + infoSheet.Columns.getByName("B").Width = 4610 + infoSheet.Columns.getByName("C").Width = 4610 + +g_exportedScripts = (NamedRanges,) +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/examples/python/SetCellColor.py b/scripting/examples/python/SetCellColor.py new file mode 100644 index 000000000..7319d621d --- /dev/null +++ b/scripting/examples/python/SetCellColor.py @@ -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 uno + +def _SetCellColor(sheet, cellRange, color): + """Sets the background of 'cellRange' in 'sheet', to 'color'.""" + # https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1table_1_1XCellRange.html#a92c77dc3025ac50d55bf31bc80ab118f + cells = sheet.getCellRangeByName(cellRange) + + # https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1table_1_1CellProperties.html + cells.CellBackColor = color + +def SetCellColor(): + ctx = uno.getComponentContext() + smgr = ctx.ServiceManager + desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) + + # Create a blank spreadsheet document, instead of operating on the existing one + doc = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, ()) + + # Select the first sheet in the spreadsheet (0-index based). + sheet = doc.Sheets[0] + + # Call the above helper function to set color (in hex number). + # To get the hex number: + # 1. go to Calc, click toolbar dropdown "Background Color" > Custom Color; + # 2. Pick a color, copy the hex number and prefix it with "0x". + _SetCellColor(sheet, "C3:C21", 0x4021c9) + _SetCellColor(sheet, "D18:E21", 0x4021c9) + _SetCellColor(sheet, "G3:G21", 0x4021c9) + _SetCellColor(sheet, "H3:I5", 0x4021c9) + _SetCellColor(sheet, "I6:I21", 0x4021c9) + _SetCellColor(sheet, "H19:H21", 0x4021c9) + + # You should get a nice "LO" in the spreadsheet! + +# Only the specified function will show in the Tools > Macro > Organize Macro dialog: +g_exportedScripts = (SetCellColor,) diff --git a/scripting/examples/python/TableSample.py b/scripting/examples/python/TableSample.py new file mode 100644 index 000000000..0920bf8ad --- /dev/null +++ b/scripting/examples/python/TableSample.py @@ -0,0 +1,131 @@ +# +# 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 uno + +# a UNO struct later needed to create a document +from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK +from com.sun.star.text.TextContentAnchorType import AS_CHARACTER +from com.sun.star.awt import Size + + +def insertTextIntoCell(table, cellName, text, color): + tableText = table.getCellByName(cellName) + cursor = tableText.createTextCursor() + + cursor.setPropertyValue("CharColor", color) + tableText.setString(text) + + +def createTable(): + """Creates a new writer document and inserts a table with some data + (also known as the SWriter sample). + """ + ctx = uno.getComponentContext() + smgr = ctx.ServiceManager + desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) + + # open a writer document + doc = desktop.loadComponentFromURL( + "private:factory/swriter", "_blank", 0, ()) + + text = doc.Text + cursor = text.createTextCursor() + text.insertString( + cursor, + "The first line in the newly created text document.\n", + 0) + text.insertString( + cursor, + "Now we are in the second line\n", + 0) + + # create a text table + table = doc.createInstance("com.sun.star.text.TextTable") + + # with 4 rows and 4 columns + table.initialize(4, 4) + + text.insertTextContent(cursor, table, 0) + rows = table.Rows + + table.setPropertyValue("BackTransparent", uno.Bool(0)) + table.setPropertyValue("BackColor", 13421823) + row = rows.getByIndex(0) + row.setPropertyValue("BackTransparent", uno.Bool(0)) + row.setPropertyValue("BackColor", 6710932) + + textColor = 16777215 + + insertTextIntoCell(table, "A1", "FirstColumn", textColor) + insertTextIntoCell(table, "B1", "SecondColumn", textColor) + insertTextIntoCell(table, "C1", "ThirdColumn", textColor) + insertTextIntoCell(table, "D1", "SUM", textColor) + + table.getCellByName("A2").setValue(22.5) + table.getCellByName("B2").setValue(5615.3) + table.getCellByName("C2").setValue(-2315.7) + table.getCellByName("D2").setFormula("sum <A2:C2>") + + table.getCellByName("A3").setValue(21.5) + table.getCellByName("B3").setValue(615.3) + table.getCellByName("C3").setValue(-315.7) + table.getCellByName("D3").setFormula("sum <A3:C3>") + + table.getCellByName("A4").setValue(121.5) + table.getCellByName("B4").setValue(-615.3) + table.getCellByName("C4").setValue(415.7) + table.getCellByName("D4").setFormula("sum <A4:C4>") + + cursor.setPropertyValue("CharColor", 255) + cursor.setPropertyValue("CharShadowed", uno.Bool(1)) + + text.insertControlCharacter(cursor, PARAGRAPH_BREAK, 0) + text.insertString( + cursor, + "This is a colored Text - blue with shadow\n", + 0) + text.insertControlCharacter(cursor, PARAGRAPH_BREAK, 0) + + textFrame = doc.createInstance("com.sun.star.text.TextFrame") + textFrame.setSize(Size(15000, 400)) + textFrame.setPropertyValue("AnchorType", AS_CHARACTER) + + text.insertTextContent(cursor, textFrame, 0) + + textInTextFrame = textFrame.getText() + cursorInTextFrame = textInTextFrame.createTextCursor() + textInTextFrame.insertString( + cursorInTextFrame, + "The first line in the newly created text frame.", + 0) + textInTextFrame.insertString( + cursorInTextFrame, + "\nWith this second line the height of the rame raises.", + 0) + text.insertControlCharacter(cursor, PARAGRAPH_BREAK, 0) + + cursor.setPropertyValue("CharColor", 65536) + cursor.setPropertyValue("CharShadowed", uno.Bool(0)) + + text.insertString(cursor, "That's all for now !!", 0) + + +g_exportedScripts = createTable, + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/java/Framework/MANIFEST.MF b/scripting/java/Framework/MANIFEST.MF new file mode 100644 index 000000000..4945b96d1 --- /dev/null +++ b/scripting/java/Framework/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.security.SecurityDialog +UNO-Type-Path: diff --git a/scripting/java/Framework/com/sun/star/script/framework/security/SecurityDialog.java b/scripting/java/Framework/com/sun/star/script/framework/security/SecurityDialog.java new file mode 100644 index 000000000..078f63395 --- /dev/null +++ b/scripting/java/Framework/com/sun/star/script/framework/security/SecurityDialog.java @@ -0,0 +1,583 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.security; + +import com.sun.star.awt.ActionEvent; +import com.sun.star.awt.ItemEvent; +import com.sun.star.awt.XButton; +import com.sun.star.awt.XCheckBox; +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlContainer; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XDialog; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XWindow; + +import com.sun.star.beans.XPropertySet; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.container.XNameContainer; + +import com.sun.star.lang.EventObject; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.lib.uno.helper.WeakBase; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.log.LogUtils; + +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.RuntimeException; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class SecurityDialog extends WeakBase implements + XComponent, XServiceInfo, XDialog, XInitialization { + + static final String __serviceName = + "com.sun.star.script.framework.security.SecurityDialog"; + + private static final String _label1Name = "Label1"; + + private static final String _label1String = + "This document contains macros. Do you want to allow these macros to be run?"; + + private static final String _label2Name = "Label2"; + + private static final String _label2String = + "This document contains macros. According to the security settings, the"; + + private static final String _label3Name = "Label3"; + + private static final String _label3String = + "macros in this document should not be run. Do you want to run them"; + + private static final String _label4Name = "Label4"; + private static final String _label4String = "anyway?"; + + private static final String _checkBoxName = "CheckBox"; + + private static final String _checkBoxString = + "Add this directory to the list of secure paths: "; + + private static final String _label5Name = "Label5"; + + private static final String _title = "Run Macro"; + private static final String _runMacro = "Run"; + private static final String _runButtonName = "Run"; + private static final String _doNotRunMacro = "Do Not Run"; + private static final String _doNotRunButtonName = "DoNotRun"; + + private static final int dialogX = 100; + private static final int dialogY = 100; + private static final int dialogW = 235; + private static final int dialogH = 47; + + private int cbIncrW = -20; + private int cbIncrH = 19; + + private static final int runButtonW = 40; + private static final int runButtonH = 13; + private static final int doNotRunButtonW = 40; + private static final int doNotRunButtonH = 13; + + // label for warning dialog + private static final int label1X = 20; + private static final int label1Y = 9; + private static final int label1W = 210; + private static final int label1H = 10; + + // labels for confirmation dialog + private static final int label2X = 22; + private static final int label2Y = 7; + private static final int label2W = 210; + private static final int label2H = 8; + private static final int label3X = 22; + private static final int label3Y = 15; + private static final int label3W = 210; + private static final int label3H = 8; + private static final int label4X = 22; + private static final int label4Y = 23; + private static final int label4W = 210; + private static final int label4H = 8; + + // checkbox for confirmation dialog + private static final int checkBoxX = 22; + private static final int checkBoxY = 40; + private static final int checkBoxW = 210; + private static final int checkBoxH = 9; + // extra label if path longer than 21 chars + private static final int label5X = 22; + private static final int label5Y = 48; + private static final int label5W = 210; + private static final int label5H = 9; + + private boolean checkBoxDialog; + private short _checkBoxState = 0; + private boolean extraPathLine = false; + private String checkBoxPath = ""; + private String checkBoxPath2 = ""; + private static final int lineWrapLength = 21; + private static final int lineWrapH = 12; + private String _pushed = _doNotRunButtonName; + + private final XComponentContext _xComponentContext; + private XDialog _xDialog; + + public SecurityDialog(XComponentContext xComponentContext) { + LogUtils.DEBUG("SecurityDialog ctor"); + _xComponentContext = xComponentContext; + } + + public void initialize(Object[] args) throws RuntimeException { + LogUtils.DEBUG("SecurityDialog init"); + + // figure out if we need a checkbox + if (args.length == 1 && AnyConverter.isString(args[0])) { + //check args is a path + // set checkBoxPath with the arg + LogUtils.DEBUG("checkbox"); + + try { + checkBoxPath = AnyConverter.toString(args[0]); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } + + LogUtils.DEBUG("path: " + checkBoxPath); + checkBoxDialog = true; + + if (checkBoxPath.length() > lineWrapLength) { + extraPathLine = true; + cbIncrH += lineWrapH; + checkBoxPath2 = checkBoxPath.substring(lineWrapLength); + checkBoxPath = checkBoxPath.substring(0, lineWrapLength); + } + + } else { + LogUtils.DEBUG("no checkbox: # of args=" + args.length); + cbIncrW = 0; + cbIncrH = 0; + checkBoxDialog = false; + } + + // now try and create the dialog + try { + _xDialog = createDialog(); + } catch (com.sun.star.uno.Exception e) { + throw new RuntimeException(e); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + // static component operations + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, XRegistryKey regKey) { + + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals(SecurityDialog.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory(SecurityDialog.class, + SecurityDialog.__serviceName, + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } + + // XServiceInfo + public String getImplementationName() { + return getClass().getName(); + } + + // XServiceInfo + public boolean supportsService(/*IN*/String serviceName) { + return serviceName.equals(__serviceName); + } + + // XServiceInfo + public String[] getSupportedServiceNames() { + String[] retValue = new String[1]; + retValue[0] = __serviceName; + return retValue; + } + + /** method for creating a dialog at runtime + */ + private XDialog createDialog() throws com.sun.star.uno.Exception { + + // get the service manager from the component context + XMultiComponentFactory xMultiComponentFactory = + _xComponentContext.getServiceManager(); + + // create the dialog model and set the properties + Object dialogModel = + xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", _xComponentContext); + + XPropertySet xPSetDialog = + UnoRuntime.queryInterface(XPropertySet.class, dialogModel); + + xPSetDialog.setPropertyValue("PositionX", Integer.valueOf(dialogX)); + xPSetDialog.setPropertyValue("PositionY", Integer.valueOf(dialogY)); + xPSetDialog.setPropertyValue("Width", Integer.valueOf(dialogW + cbIncrW)); + xPSetDialog.setPropertyValue("Height", Integer.valueOf(dialogH + cbIncrH)); + xPSetDialog.setPropertyValue("Title", _title); + + // get the service manager from the dialog model + XMultiServiceFactory xMultiServiceFactory = + UnoRuntime.queryInterface(XMultiServiceFactory.class, dialogModel); + + // create the Run Macro button model and set the properties + Object runButtonModel = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlButtonModel"); + + XPropertySet xPSetButton = + UnoRuntime.queryInterface(XPropertySet.class, runButtonModel); + + LogUtils.DEBUG("run: x=" + (((dialogW + cbIncrW) / 2) - runButtonW - 1)); + LogUtils.DEBUG("run: y=" + (dialogH + cbIncrH - runButtonH - 1)); + + xPSetButton.setPropertyValue( + "PositionX", Integer.valueOf((((dialogW + cbIncrW) / 2) - runButtonW - 1))); + + xPSetButton.setPropertyValue( + "PositionY", Integer.valueOf(dialogH + cbIncrH - runButtonH - 1)); + + xPSetButton.setPropertyValue("Width", Integer.valueOf(runButtonW)); + xPSetButton.setPropertyValue("Height", Integer.valueOf(runButtonH)); + xPSetButton.setPropertyValue("Name", _runButtonName); + xPSetButton.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetButton.setPropertyValue("Label", _runMacro); + + // create the Don't Run Macro button model and set the properties + Object doNotRunButtonModel = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlButtonModel"); + + xPSetButton = + UnoRuntime.queryInterface(XPropertySet.class, doNotRunButtonModel); + + LogUtils.DEBUG("dontrun: x=" + (((dialogW + cbIncrW) / 2) - 1)); + LogUtils.DEBUG("dontrun: y=" + (dialogH + cbIncrH - doNotRunButtonH - 1)); + + xPSetButton.setPropertyValue( + "PositionX", Integer.valueOf((((dialogW + cbIncrW) / 2) + 1))); + + xPSetButton.setPropertyValue( + "PositionY", Integer.valueOf((dialogH + cbIncrH - doNotRunButtonH - 1))); + + xPSetButton.setPropertyValue("Width", Integer.valueOf(doNotRunButtonW)); + xPSetButton.setPropertyValue("Height", Integer.valueOf(doNotRunButtonH)); + xPSetButton.setPropertyValue("Name", _doNotRunButtonName); + xPSetButton.setPropertyValue("TabIndex", Short.valueOf((short)0)); + xPSetButton.setPropertyValue("Label", _doNotRunMacro); + + // insert the control models into the dialog model + XNameContainer xNameCont = + UnoRuntime.queryInterface(XNameContainer.class, dialogModel); + + xNameCont.insertByName(_runButtonName, runButtonModel); + xNameCont.insertByName(_doNotRunButtonName, doNotRunButtonModel); + + if (checkBoxDialog) { + LogUtils.DEBUG("creating label & checkbox"); + + // create the label model and set the properties + Object label2Model = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + XPropertySet xPSetLabel = + UnoRuntime.queryInterface(XPropertySet.class, label2Model); + + xPSetLabel.setPropertyValue("PositionX", Integer.valueOf(label2X)); + xPSetLabel.setPropertyValue("PositionY", Integer.valueOf(label2Y)); + xPSetLabel.setPropertyValue("Width", Integer.valueOf(label2W)); + xPSetLabel.setPropertyValue("Height", Integer.valueOf(label2H)); + xPSetLabel.setPropertyValue("Name", _label2Name); + xPSetLabel.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetLabel.setPropertyValue("Label", _label2String); + + // create the label model and set the properties + Object label3Model = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + XPropertySet xPSetLabel3 = + UnoRuntime.queryInterface(XPropertySet.class, label3Model); + + xPSetLabel3.setPropertyValue("PositionX", Integer.valueOf(label3X)); + xPSetLabel3.setPropertyValue("PositionY", Integer.valueOf(label3Y)); + xPSetLabel3.setPropertyValue("Width", Integer.valueOf(label3W)); + xPSetLabel3.setPropertyValue("Height", Integer.valueOf(label3H)); + xPSetLabel3.setPropertyValue("Name", _label3Name); + xPSetLabel3.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetLabel3.setPropertyValue("Label", _label3String); + + // create the label model and set the properties + Object label4Model = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + XPropertySet xPSetLabel4 = + UnoRuntime.queryInterface(XPropertySet.class, label4Model); + + xPSetLabel4.setPropertyValue("PositionX", Integer.valueOf(label4X)); + xPSetLabel4.setPropertyValue("PositionY", Integer.valueOf(label4Y)); + xPSetLabel4.setPropertyValue("Width", Integer.valueOf(label4W)); + xPSetLabel4.setPropertyValue("Height", Integer.valueOf(label4H)); + xPSetLabel4.setPropertyValue("Name", _label4Name); + xPSetLabel4.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetLabel4.setPropertyValue("Label", _label4String); + + // create the checkbox model and set the properties + Object checkBoxModel = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlCheckBoxModel"); + + XPropertySet xPSetCheckBox = + UnoRuntime.queryInterface(XPropertySet.class, checkBoxModel); + + xPSetCheckBox.setPropertyValue("PositionX", Integer.valueOf(checkBoxX)); + xPSetCheckBox.setPropertyValue("PositionY", Integer.valueOf(checkBoxY)); + xPSetCheckBox.setPropertyValue("Width", Integer.valueOf(checkBoxW)); + xPSetCheckBox.setPropertyValue("Height", Integer.valueOf(checkBoxH)); + xPSetCheckBox.setPropertyValue("State", Short.valueOf((short)0)); + xPSetCheckBox.setPropertyValue("Name", _checkBoxName); + xPSetCheckBox.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetCheckBox.setPropertyValue("Label", _checkBoxString + checkBoxPath); + + // insert the control models into the dialog model + xNameCont.insertByName(_label2Name, label2Model); + xNameCont.insertByName(_label3Name, label3Model); + xNameCont.insertByName(_label4Name, label4Model); + xNameCont.insertByName(_checkBoxName, checkBoxModel); + + if (extraPathLine) { + + // create the label model and set the properties + Object label5Model = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + XPropertySet xPSetLabel5 = + UnoRuntime.queryInterface(XPropertySet.class, label5Model); + + xPSetLabel5.setPropertyValue("PositionX", Integer.valueOf(label5X)); + xPSetLabel5.setPropertyValue("PositionY", Integer.valueOf(label5Y)); + xPSetLabel5.setPropertyValue("Width", Integer.valueOf(label5W)); + xPSetLabel5.setPropertyValue("Height", Integer.valueOf(label5H)); + xPSetLabel5.setPropertyValue("Name", _label5Name); + xPSetLabel5.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetLabel5.setPropertyValue("Label", checkBoxPath2); + xNameCont.insertByName(_label5Name, label5Model); + } + } else { + // create the label model and set the properties + Object labelModel = + xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + XPropertySet xPSetLabel = + UnoRuntime.queryInterface(XPropertySet.class, labelModel); + + xPSetLabel.setPropertyValue("PositionX", Integer.valueOf(label1X)); + xPSetLabel.setPropertyValue("PositionY", Integer.valueOf(label1Y)); + xPSetLabel.setPropertyValue("Width", Integer.valueOf(label1W)); + xPSetLabel.setPropertyValue("Height", Integer.valueOf(label1H)); + xPSetLabel.setPropertyValue("Name", _label1Name); + xPSetLabel.setPropertyValue("TabIndex", Short.valueOf((short)1)); + xPSetLabel.setPropertyValue("Label", _label1String); + + // insert the control models into the dialog model + xNameCont.insertByName(_label1Name, labelModel); + } + + // create the dialog control and set the model + Object dialog = + xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.UnoControlDialog", _xComponentContext); + + XControl xControl = + UnoRuntime.queryInterface(XControl.class, dialog); + + XControlModel xControlModel = + UnoRuntime.queryInterface(XControlModel.class, dialogModel); + + xControl.setModel(xControlModel); + + // add an action listener to the button control + XControlContainer xControlCont = + UnoRuntime.queryInterface(XControlContainer.class, dialog); + + // Add to yes button + Object objectButton = xControlCont.getControl(_runButtonName); + + XButton xButton = + UnoRuntime.queryInterface(XButton.class, objectButton); + + xButton.addActionListener(new ActionListenerImpl(_runButtonName)); + + // add to no button + objectButton = xControlCont.getControl(_doNotRunButtonName); + + xButton = + UnoRuntime.queryInterface(XButton.class, objectButton); + + xButton.addActionListener(new ActionListenerImpl(_doNotRunButtonName)); + + if (checkBoxDialog) { + // add to checkbox + Object objectCheckBox = xControlCont.getControl(_checkBoxName); + + XCheckBox xCheckBox = + UnoRuntime.queryInterface(XCheckBox.class, objectCheckBox); + + xCheckBox.addItemListener(new ItemListenerImpl(xControlCont)); + } + + // create a peer + Object toolkit = + xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.ExtToolkit", _xComponentContext); + + XToolkit xToolkit = UnoRuntime.queryInterface(XToolkit.class, toolkit); + XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, xControl); + xWindow.setVisible(false); + xControl.createPeer(xToolkit, null); + + // return the dialog + XDialog xDialog = UnoRuntime.queryInterface(XDialog.class, dialog); + return xDialog; + } + + public short execute() { + + short result = 0; + _pushed = _doNotRunButtonName; + LogUtils.DEBUG("*DF* Before execute "); + _xDialog.execute(); + LogUtils.DEBUG("*DF* After execute "); + + if (_pushed.equals(_runButtonName)) { + result += 1; + } + + if (_checkBoxState == 1) { + result += 2; + } + + return result; + } + + public void endExecute() { + _xDialog.endExecute(); + } + + public String getTitle() { + return _xDialog.getTitle(); + } + + public void setTitle(String Title) { + _xDialog.setTitle(Title); + } + + public void dispose() { + + XComponent xComponent = + UnoRuntime.queryInterface(XComponent.class, _xDialog); + + xComponent.dispose(); + } + + public void addEventListener(com.sun.star.lang.XEventListener xListener) { + + XComponent xComponent = + UnoRuntime.queryInterface(XComponent.class, _xDialog); + + xComponent.addEventListener(xListener); + } + + public void removeEventListener(com.sun.star.lang.XEventListener aListener) { + + XComponent xComponent = + UnoRuntime.queryInterface(XComponent.class, _xDialog); + + xComponent.removeEventListener(aListener); + } + + private class ActionListenerImpl implements com.sun.star.awt.XActionListener { + + private final String _buttonName; + + private ActionListenerImpl(String buttonName) { + _buttonName = buttonName; + } + + // XEventListener + public void disposing(EventObject eventObject) { + } + + // XActionListener + public void actionPerformed(ActionEvent actionEvent) { + _pushed = _buttonName; + LogUtils.DEBUG("** Button pushed ->" + _pushed); + _xDialog.endExecute(); + } + } + + private class ItemListenerImpl implements com.sun.star.awt.XItemListener { + + private XCheckBox _xCheckBox; + + private ItemListenerImpl(XControlContainer xControlCont) { + Object objectCheckBox = xControlCont.getControl(_checkBoxName); + + _xCheckBox = + UnoRuntime.queryInterface(XCheckBox.class, objectCheckBox); + + } + + // XEventListener + public void disposing(EventObject eventObject) { + _xCheckBox = null; + } + + // XAdjustmentListener + public void itemStateChanged(ItemEvent itemEvent) { + _checkBoxState = _xCheckBox.getState(); + LogUtils.DEBUG("** checkbox state ->" + _checkBoxState); + } + } +} diff --git a/scripting/java/ScriptFramework.component b/scripting/java/ScriptFramework.component new file mode 100644 index 000000000..3a81f7bd8 --- /dev/null +++ b/scripting/java/ScriptFramework.component @@ -0,0 +1,25 @@ +<?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.Java2" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.script.framework.security.SecurityDialog"> + <service name="com.sun.star.script.framework.security.SecurityDialog"/> + </implementation> +</component> diff --git a/scripting/java/ScriptProviderForBeanShell.component b/scripting/java/ScriptProviderForBeanShell.component new file mode 100644 index 000000000..c6ea8c404 --- /dev/null +++ b/scripting/java/ScriptProviderForBeanShell.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.Java2" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.script.framework.provider.beanshell.ScriptProviderForBeanShell$ScriptProviderForBeanShell_2"> + <service name="com.sun.star.script.browse.BrowseNode"/> + <service name="com.sun.star.script.provider.LanguageScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProviderForBeanShell"/> + </implementation> +</component> diff --git a/scripting/java/ScriptProviderForJava.component b/scripting/java/ScriptProviderForJava.component new file mode 100644 index 000000000..1ad936b17 --- /dev/null +++ b/scripting/java/ScriptProviderForJava.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.Java2" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.script.framework.provider.java.ScriptProviderForJava$_ScriptProviderForJava"> + <service name="com.sun.star.script.browse.BrowseNode"/> + <service name="com.sun.star.script.provider.LanguageScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProviderForJava"/> + </implementation> +</component> diff --git a/scripting/java/ScriptProviderForJavaScript.component b/scripting/java/ScriptProviderForJavaScript.component new file mode 100644 index 000000000..6e6f1f4c1 --- /dev/null +++ b/scripting/java/ScriptProviderForJavaScript.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.Java2" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.script.framework.provider.javascript.ScriptProviderForJavaScript$ScriptProviderForJavaScript_2"> + <service name="com.sun.star.script.browse.BrowseNode"/> + <service name="com.sun.star.script.provider.LanguageScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProviderForJavaScript"/> + </implementation> +</component> diff --git a/scripting/java/com/sun/star/script/framework/browse/DialogFactory.java b/scripting/java/com/sun/star/script/framework/browse/DialogFactory.java new file mode 100644 index 000000000..08cfba4c8 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/DialogFactory.java @@ -0,0 +1,255 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.browse; + +import com.sun.star.awt.XButton; +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlContainer; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XDialog; +import com.sun.star.awt.XTextComponent; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XWindow; + +import com.sun.star.beans.XPropertySet; + +import com.sun.star.container.XNameContainer; + +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class DialogFactory { + + private static DialogFactory factory; + private final XComponentContext xComponentContext; + + // singleton + private DialogFactory(XComponentContext xComponentContext) { + this.xComponentContext = xComponentContext; + } + + public static void createDialogFactory(XComponentContext xComponentContext) { + synchronized (DialogFactory.class) { + if (factory == null) { + factory = new DialogFactory(xComponentContext); + } + } + } + + public static DialogFactory getDialogFactory() throws java.lang.Exception { + if (factory == null) { + throw new java.lang.Exception("DialogFactory not initialized"); + } + return factory; + } + + public String showInputDialog(String title, String prompt) { + final XDialog xDialog; + + try { + xDialog = createInputDialog(title, prompt); + } catch (com.sun.star.uno.Exception e) { + return null; + } + + // add an action listener to the button controls + XControlContainer controls = + UnoRuntime.queryInterface(XControlContainer.class, xDialog); + + XButton okButton = + UnoRuntime.queryInterface(XButton.class, controls.getControl("Ok")); + + okButton.setActionCommand("Ok"); + + XButton cancelButton = + UnoRuntime.queryInterface(XButton.class, controls.getControl("Cancel")); + + cancelButton.setActionCommand("Cancel"); + + final XTextComponent textField = + UnoRuntime.queryInterface(XTextComponent.class, + controls.getControl("NameField")); + + final ResultHolder resultHolder = new ResultHolder(); + + com.sun.star.awt.XActionListener listener = + new com.sun.star.awt.XActionListener() { + public void actionPerformed(com.sun.star.awt.ActionEvent e) { + if (e.ActionCommand.equals("Cancel")) { + resultHolder.setResult(null); + xDialog.endExecute(); + } else { + resultHolder.setResult(textField.getText()); + xDialog.endExecute(); + } + } + + public void disposing(EventObject o) { + // does nothing + } + }; + + okButton.addActionListener(listener); + cancelButton.addActionListener(listener); + + xDialog.execute(); + + return (String)resultHolder.getResult(); + } + + private void setDimensions(Object o, int x, int y, int width, int height) throws + com.sun.star.uno.Exception { + + XPropertySet props = UnoRuntime.queryInterface(XPropertySet.class, o); + + props.setPropertyValue("PositionX", Integer.valueOf(x)); + props.setPropertyValue("PositionY", Integer.valueOf(y)); + props.setPropertyValue("Height", Integer.valueOf(height)); + props.setPropertyValue("Width", Integer.valueOf(width)); + } + + private XDialog createInputDialog(String title, String prompt) throws + com.sun.star.uno.Exception { + + if (title == null || title.length() == 0) { + title = "Scripting Framework"; + } + + if (prompt == null || prompt.length() == 0) { + prompt = "Enter name"; + } + + // get the service manager from the component context + XMultiComponentFactory xMultiComponentFactory = + xComponentContext.getServiceManager(); + + // create the dialog model and set the properties + Object dialogModel = xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", + xComponentContext); + + setDimensions(dialogModel, 100, 100, 157, 58); + + XPropertySet props = + UnoRuntime.queryInterface(XPropertySet.class, dialogModel); + + props.setPropertyValue("Title", title); + + // get the service manager from the dialog model + XMultiServiceFactory xMultiServiceFactory = + UnoRuntime.queryInterface(XMultiServiceFactory.class, dialogModel); + + // create the label model and set the properties + Object label = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlFixedTextModel"); + + setDimensions(label, 15, 5, 134, 12); + + XPropertySet labelProps = + UnoRuntime.queryInterface(XPropertySet.class, label); + + labelProps.setPropertyValue("Name", "PromptLabel"); + labelProps.setPropertyValue("Label", prompt); + + // create the text field + Object edit = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlEditModel"); + + setDimensions(edit, 15, 18, 134, 12); + + XPropertySet editProps = + UnoRuntime.queryInterface(XPropertySet.class, edit); + + editProps.setPropertyValue("Name", "NameField"); + + // create the OK button + Object okButtonModel = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlButtonModel"); + + setDimensions(okButtonModel, 40, 39, 38, 15); + + XPropertySet buttonProps = + UnoRuntime.queryInterface(XPropertySet.class, okButtonModel); + + buttonProps.setPropertyValue("Name", "Ok"); + buttonProps.setPropertyValue("Label", "Ok"); + + // create the Cancel button + Object cancelButtonModel = xMultiServiceFactory.createInstance( + "com.sun.star.awt.UnoControlButtonModel"); + + setDimensions(cancelButtonModel, 83, 39, 38, 15); + + buttonProps = + UnoRuntime.queryInterface(XPropertySet.class, cancelButtonModel); + + buttonProps.setPropertyValue("Name", "Cancel"); + buttonProps.setPropertyValue("Label", "Cancel"); + + // insert the control models into the dialog model + XNameContainer xNameCont = + UnoRuntime.queryInterface(XNameContainer.class, dialogModel); + + xNameCont.insertByName("PromptLabel", label); + xNameCont.insertByName("NameField", edit); + xNameCont.insertByName("Ok", okButtonModel); + xNameCont.insertByName("Cancel", cancelButtonModel); + + // create the dialog control and set the model + Object dialog = xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.UnoControlDialog", + xComponentContext); + + XControl xControl = UnoRuntime.queryInterface(XControl.class, dialog); + + XControlModel xControlModel = + UnoRuntime.queryInterface(XControlModel.class, dialogModel); + + xControl.setModel(xControlModel); + + // create a peer + Object toolkit = xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.awt.ExtToolkit", + xComponentContext); + + XToolkit xToolkit = UnoRuntime.queryInterface(XToolkit.class, toolkit); + XWindow xWindow = UnoRuntime.queryInterface(XWindow.class, xControl); + xWindow.setVisible(false); + xControl.createPeer(xToolkit, null); + + return UnoRuntime.queryInterface(XDialog.class, dialog); + } + + private static class ResultHolder { + + private Object result = null; + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java new file mode 100644 index 000000000..cdb8715a5 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/ParcelBrowseNode.java @@ -0,0 +1,309 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.browse; + +import com.sun.star.beans.XIntrospectionAccess; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.container.Parcel; +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.container.ScriptEntry; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.swing.JOptionPane; + +public class ParcelBrowseNode extends PropertySet implements + XBrowseNode, XInvocation { + + private final ScriptProvider provider; + private Collection<XBrowseNode> browsenodes; + private final ParcelContainer container; + private Parcel parcel; + // these four are properties, they are accessed via reflection + public boolean deletable = true; + public boolean editable = false; + public boolean creatable = false; + public boolean renamable = true; + + public ParcelBrowseNode(ScriptProvider provider, ParcelContainer container, + String parcelName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + this.provider = provider; + this.container = container; + + this.parcel = (Parcel)this.container.getByName(parcelName); + + registerProperty("Deletable", new Type(boolean.class), (short)0, "deletable"); + registerProperty("Editable", new Type(boolean.class), (short)0, "editable"); + registerProperty("Creatable", new Type(boolean.class), (short)0, "creatable"); + registerProperty("Renamable", new Type(boolean.class), (short)0, "renamable"); + if (provider.hasScriptEditor()) + { + this.creatable = true; + } + + String parcelDirUrl = parcel.getPathToParcel(); + XComponentContext xCtx = provider.getScriptingContext().getComponentContext(); + XMultiComponentFactory xFac = xCtx.getServiceManager(); + + try { + XSimpleFileAccess xSFA = UnoRuntime.queryInterface(XSimpleFileAccess.class, + xFac.createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", + xCtx)); + if ( xSFA != null && ( xSFA.isReadOnly( parcelDirUrl ) || + container.isUnoPkg() ) ) + { + deletable = false; + editable = false; + creatable = false; + renamable = false; + } + } catch (com.sun.star.uno.Exception e) { + // TODO propagate potential errors + LogUtils.DEBUG("Caught exception creating ParcelBrowseNode " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + + } + + public String getName() { + return parcel.getName(); + } + + public XBrowseNode[] getChildNodes() { + try { + + if (hasChildNodes()) { + String[] names = parcel.getElementNames(); + browsenodes = new ArrayList<XBrowseNode>(names.length); + + for (String name : names) { + browsenodes.add(new ScriptBrowseNode(provider, parcel, name)); + } + } else { + LogUtils.DEBUG("ParcelBrowseNode.getChildeNodes no children "); + return new XBrowseNode[0]; + } + } catch (Exception e) { + LogUtils.DEBUG("Failed to getChildeNodes, exception: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + return new XBrowseNode[0]; + } + + return browsenodes.toArray(new XBrowseNode[browsenodes.size()]); + } + + public boolean hasChildNodes() { + if (container != null && parcel != null && container.hasByName(getName())) { + return parcel.hasElements(); + } + + return false; + } + + public short getType() { + return BrowseNodeTypes.CONTAINER; + } + + @Override + public String toString() { + return getName(); + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return null; + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + LogUtils.DEBUG("ParcelBrowseNode invoke for " + aFunctionName); + + // Initialise the out parameters - not used but prevents error in + // UNO bridge + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + Any result = new Any(new Type(Boolean.class), Boolean.TRUE); + + if (aFunctionName.equals("Creatable")) { + try { + String newName; + + if (aParams == null || aParams.length < 1 + || !AnyConverter.isString(aParams[0])) { + + String prompt = "Enter name for new Script"; + String title = "Create Script"; + + // try to get a DialogFactory instance, if it fails + // just use a Swing JOptionPane to prompt for the name + try { + DialogFactory dialogFactory = DialogFactory.getDialogFactory(); + newName = dialogFactory.showInputDialog(title, prompt); + } catch (Exception e) { + + newName = JOptionPane.showInputDialog(null, prompt, title, + JOptionPane.QUESTION_MESSAGE); + + } + } else { + newName = AnyConverter.toString(aParams[0]); + } + + if (newName == null || newName.length() == 0) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } else { + String source = provider.getScriptEditor().getTemplate(); + + String languageName = + newName + "." + provider.getScriptEditor().getExtension(); + + String language = container.getLanguage(); + + ScriptEntry entry = new ScriptEntry(language, languageName); + + Parcel parcel = (Parcel)container.getByName(getName()); + ScriptMetaData data = new ScriptMetaData(parcel, entry, source); + parcel.insertByName(languageName, data); + + ScriptBrowseNode sbn = + new ScriptBrowseNode(provider, parcel, languageName); + + if (browsenodes == null) { + LogUtils.DEBUG("browsenodes null!!"); + browsenodes = new ArrayList<XBrowseNode>(4); + } + + browsenodes.add(sbn); + result = new Any(new Type(XBrowseNode.class), sbn); + } + } catch (Exception e) { + LogUtils.DEBUG("ParcelBrowseNode[create] failed with: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } else if (aFunctionName.equals("Deletable")) { + try { + if (container.deleteParcel(getName())) { + result = new Any(new Type(Boolean.class), Boolean.TRUE); + } else { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } catch (Exception e) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } else if (aFunctionName.equals("Renamable")) { + + String newName = null; + + try { + + if (aParams == null || aParams.length < 1 || + !AnyConverter.isString(aParams[0])) { + String prompt = "Enter new name for Library"; + String title = "Rename Library"; + + // try to get a DialogFactory instance, if it fails + // just use a Swing JOptionPane to prompt for the name + try { + DialogFactory dialogFactory = DialogFactory.getDialogFactory(); + newName = dialogFactory.showInputDialog(title, prompt); + } catch (Exception e) { + + newName = JOptionPane.showInputDialog(null, prompt, title, + JOptionPane.QUESTION_MESSAGE); + + } + } else { + newName = AnyConverter.toString(aParams[0]); + } + + container.renameParcel(getName(), newName); + Parcel p = (Parcel)container.getByName(newName); + + if (browsenodes == null) { + getChildNodes(); + } + + ScriptBrowseNode[] childNodes = + browsenodes.toArray(new ScriptBrowseNode[browsenodes.size()]); + + for (int index = 0; index < childNodes.length; index++) { + childNodes[ index ].updateURI(p); + } + + result = new Any(new Type(XBrowseNode.class), this); + } catch (Exception e) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } + + else { + throw new com.sun.star.lang.IllegalArgumentException( + "Function " + aFunctionName + " not supported."); + } + + return result; + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + return null; + } + + public boolean hasMethod(String aName) { + return false; + } + + public boolean hasProperty(String aName) { + return false; + } +} diff --git a/scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode.java new file mode 100644 index 000000000..6cdfd2a77 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/PkgProviderBrowseNode.java @@ -0,0 +1,49 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.browse; + +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; + +import com.sun.star.uno.XComponentContext; + +public class PkgProviderBrowseNode extends ProviderBrowseNode { + + public PkgProviderBrowseNode(ScriptProvider provider, ParcelContainer container, + XComponentContext xCtx) { + + super(provider, container, xCtx); + + LogUtils.DEBUG("*** PkgProviderBrowseNode ctor container name = " + + container.getName()); + LogUtils.DEBUG("*** PkgProviderBrowseNode ctor container path = " + + container.getParcelContainerDir()); + LogUtils.DEBUG("*** PkgProviderBrowseNode ctor, container has num parcels = " + + container.getElementNames().length); + deletable = false; + editable = false; + creatable = false; + } + + @Override public String getName() { + return (container != null) ? container.getName() : "Unknown"; + } + +} diff --git a/scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode.java new file mode 100644 index 000000000..91c2566be --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/ProviderBrowseNode.java @@ -0,0 +1,264 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.browse; + +import com.sun.star.beans.XIntrospectionAccess; + +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.swing.JOptionPane; + +public class ProviderBrowseNode extends PropertySet implements + XBrowseNode, XInvocation { + + private final ScriptProvider provider; + private Collection<XBrowseNode> browsenodes; + private final String name; + protected ParcelContainer container; + private final XComponentContext m_xCtx; + // these are properties, they are accessed by reflection + public boolean deletable = true; + public boolean creatable = true; + public boolean editable = false; + + public ProviderBrowseNode(ScriptProvider provider, ParcelContainer container, + XComponentContext xCtx) { + + LogUtils.DEBUG("*** ProviderBrowseNode ctor"); + this.container = container; + this.name = this.container.getLanguage(); + this.provider = provider; + this.m_xCtx = xCtx; + + registerProperty("Deletable", new Type(boolean.class), (short)0, "deletable"); + registerProperty("Creatable", new Type(boolean.class), (short)0, "creatable"); + registerProperty("Editable", new Type(boolean.class), (short)0, "editable"); + XMultiComponentFactory xFac = m_xCtx.getServiceManager(); + + try { + XSimpleFileAccess xSFA = UnoRuntime.queryInterface(XSimpleFileAccess.class, + xFac.createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", + xCtx)); + if ( container.isUnoPkg() || xSFA.isReadOnly( container.getParcelContainerDir() ) ) + { + deletable = false; + creatable = false; + } + } + // TODO propagate errors + catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("Caught exception in creation of ProviderBrowseNode "); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + + public String getName() { + return name; + } + + public XBrowseNode[] getChildNodes() { + LogUtils.DEBUG("***** ProviderBrowseNode.getChildNodes()"); + + if (hasChildNodes()) { + + // needs initialisation? + LogUtils.DEBUG("** ProviderBrowseNode.getChildNodes(), container is " + + container); + + String[] parcels = container.getElementNames(); + browsenodes = new ArrayList<XBrowseNode>(parcels.length); + + for (String parcel : parcels) { + try { + XBrowseNode node = new ParcelBrowseNode(provider, container, parcel); + browsenodes.add(node); + } catch (Exception e) { + LogUtils.DEBUG("*** Failed to create parcel node for " + parcel); + LogUtils.DEBUG(e.toString()); + } + } + + ParcelContainer[] packageContainers = container.getChildContainers(); + + LogUtils.DEBUG("**** For container named " + container.getName() + + " with root path " + container.getParcelContainerDir() + + " has " + packageContainers.length + " child containers "); + + for (ParcelContainer packageContainer : packageContainers) { + + XBrowseNode node = + new PkgProviderBrowseNode(provider, packageContainer, m_xCtx); + + browsenodes.add(node); + } + } else { + LogUtils.DEBUG("*** No container available"); + return new XBrowseNode[0]; + } + + return browsenodes.toArray(new XBrowseNode[browsenodes.size()]); + } + + public boolean hasChildNodes() { + boolean result = true; + + if (container == null || + (!container.hasElements() && container.getChildContainers().length == 0)) { + + result = false; + } + + if (container == null) { + LogUtils.DEBUG("***** ProviderBrowseNode.hasChildNodes(): " + "name=" + name + + ", path=<none>, result=" + result); + } else { + LogUtils.DEBUG("***** ProviderBrowseNode.hasChildNodes(): " + "name=" + name + + ", path=" + container.getParcelContainerDir() + ", result=" + result); + } + + return result; + } + + public short getType() { + return BrowseNodeTypes.CONTAINER; + } + + @Override + public String toString() { + return getName(); + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return null; + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + // Initialise the out parameters - not used but prevents error in + // UNO bridge + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + Any result = new Any(new Type(Boolean.class), Boolean.TRUE); + + if (aFunctionName.equals("Creatable")) { + try { + String name; + + if (aParams == null || aParams.length < 1 || + !AnyConverter.isString(aParams[0])) { + + String prompt = "Enter name for new Parcel"; + String title = "Create Parcel"; + + // try to get a DialogFactory instance, if it fails + // just use a Swing JOptionPane to prompt for the name + try { + DialogFactory dialogFactory = DialogFactory.getDialogFactory(); + name = dialogFactory.showInputDialog(title, prompt); + } catch (Exception e) { + + name = JOptionPane.showInputDialog(null, prompt, title, + JOptionPane.QUESTION_MESSAGE); + + } + } else { + name = AnyConverter.toString(aParams[0]); + } + + if (name == null || name.length() == 0) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } else { + + Object newParcel = container.createParcel(name); + LogUtils.DEBUG("Parcel created " + name + " " + newParcel); + + if (newParcel == null) { + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } else { + ParcelBrowseNode parcel = new ParcelBrowseNode(provider, container, name); + LogUtils.DEBUG("created parcel node "); + + if (browsenodes == null) { + browsenodes = new ArrayList<XBrowseNode>(5); + } + + browsenodes.add(parcel); + result = new Any(new Type(XBrowseNode.class), parcel); + } + } + } catch (Exception e) { + LogUtils.DEBUG("ProviderBrowseNode[create] failed with: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + result = new Any(new Type(Boolean.class), Boolean.FALSE); + } + } else { + throw new com.sun.star.lang.IllegalArgumentException( + "Function " + aFunctionName + " not supported."); + } + + return result; + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + + return null; + } + + public boolean hasMethod(String aName) { + return false; + } + + public boolean hasProperty(String aName) { + return false; + } +} diff --git a/scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode.java b/scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode.java new file mode 100644 index 000000000..5f91b1a78 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/browse/ScriptBrowseNode.java @@ -0,0 +1,300 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.browse; + +import com.sun.star.beans.XIntrospectionAccess; + +import com.sun.star.container.ElementExistException; +import com.sun.star.container.NoSuchElementException; + +import com.sun.star.lang.NoSupportException; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.reflection.InvocationTargetException; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.container.Parcel; +import com.sun.star.script.framework.container.ScriptEntry; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class ScriptBrowseNode extends PropertySet implements + XBrowseNode, XInvocation { + + private final ScriptProvider provider; + + private Parcel parent; + private String name; + + // these are properties, accessed by reflection + public String uri; + public String description; + public boolean editable; + public boolean deletable = false; + public boolean renamable = false; + + public ScriptBrowseNode(ScriptProvider provider, Parcel parent, String name) { + + this.provider = provider; + this.name = name; + this.parent = parent; + ScriptMetaData data = null; + XComponentContext xCtx = provider.getScriptingContext().getComponentContext(); + XMultiComponentFactory xFac = xCtx.getServiceManager(); + + try { + data = parent.getByName( name ); + XSimpleFileAccess xSFA = UnoRuntime.queryInterface( + XSimpleFileAccess.class, + xFac.createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", + xCtx)); + + uri = data.getShortFormScriptURL(); + description = data.getDescription(); + if (provider.hasScriptEditor()) { + this.editable = true; + + try { + if (!parent.isUnoPkg() + && !xSFA.isReadOnly(parent.getPathToParcel())) { + + this.deletable = true; + this.renamable = true; + + } + } + // TODO propagate errors + catch (Exception e) { + LogUtils.DEBUG("Caught exception in creation of ScriptBrowseNode"); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + + } + + } + // TODO fix exception types to be caught here, should we rethrow? + catch (Exception e) { + + LogUtils.DEBUG("** caught exception getting script data for " + name + + " ->" + e.toString()); + + } + + registerProperty("Deletable", new Type(boolean.class), (short)0, "deletable"); + registerProperty("Editable", new Type(boolean.class), (short)0, "editable"); + registerProperty("Renamable", new Type(boolean.class), (short)0, "renamable"); + registerProperty("URI", new Type(String.class), (short)0, "uri"); + registerProperty("Description", new Type(String.class), (short)0, + "description"); + } + + public String getName() { + return name; + } + + public XBrowseNode[] getChildNodes() { + return new XBrowseNode[0]; + } + + public boolean hasChildNodes() { + return false; + } + + public short getType() { + return BrowseNodeTypes.SCRIPT; + } + + @Override + public String toString() { + return getName(); + } + + public void updateURI(Parcel p) { + parent = p; + + try { + ScriptMetaData data = parent.getByName(name); + uri = data.getShortFormScriptURL(); + } + // TODO fix exception types to be caught here, should we rethrow? + catch (Exception e) { + LogUtils.DEBUG("** caught exception getting script data for " + name + + " ->" + e.toString()); + } + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return null; + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + // Initialise the out parameters - not used but prevents error in + // UNO bridge + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + Any result = new Any(new Type(Boolean.class), Boolean.TRUE); + + if (aFunctionName.equals("Editable")) { + if (!editable) { + + NoSupportException nse = + new NoSupportException(aFunctionName + " is not editable "); + + throw new InvocationTargetException( + "Scripting framework error editing script", null, nse); + + } + + XScriptContext ctxt = provider.getScriptingContext(); + ScriptMetaData data = null; + + try { + data = parent.getByName(name); + } catch (NoSuchElementException nse) { + throw new com.sun.star.lang.IllegalArgumentException(nse, + name + " does not exist or can't be found "); + } catch (com.sun.star.lang.WrappedTargetException wte) { + // rethrow + throw new InvocationTargetException( + "Scripting framework editing script ", null, wte.TargetException); + } + + provider.getScriptEditor().edit(ctxt, data); + } else if (aFunctionName.equals("Deletable")) { + if (!deletable) { + + NoSupportException nse = new NoSupportException( + aFunctionName + " is not supported for this node"); + + throw new InvocationTargetException( + "Scripting framework error deleting script", null, nse); + } + + try { + parent.removeByName(name); + result = new Any(new Type(Boolean.class), Boolean.TRUE); + } catch (NoSuchElementException nse) { + throw new com.sun.star.lang.IllegalArgumentException(nse, + name + " does not exist or can't be found "); + } catch (WrappedTargetException wte) { + // rethrow + throw new InvocationTargetException( + "Scripting framework deleting script ", null, wte.TargetException); + } + + } else if (aFunctionName.equals("Renamable")) { + result = new Any(new Type(XBrowseNode.class), new XBrowseNode[0]); + + if (!renamable) { + + NoSupportException nse = new NoSupportException( + aFunctionName + " is not supported for this node"); + + throw new InvocationTargetException( + "Scripting framework error renaming script", null, nse); + } + + try { + String newName = AnyConverter.toString(aParams[0]); + ScriptMetaData oldData = parent.getByName(name); + oldData.loadSource(); + String oldSource = oldData.getSource(); + + LogUtils.DEBUG("Create renamed script"); + + String languageName = + newName + "." + provider.getScriptEditor().getExtension(); + + String language = provider.getName(); + + ScriptEntry entry = new ScriptEntry(language, languageName); + + ScriptMetaData data = + new ScriptMetaData(parent, entry, oldSource); + + parent.insertByName(languageName, data); + + LogUtils.DEBUG("Now remove old script"); + parent.removeByName(name); + + uri = data.getShortFormScriptURL(); + name = languageName; + result = new Any(new Type(XBrowseNode.class), this); + } catch (NoSuchElementException nse) { + throw new com.sun.star.lang.IllegalArgumentException(nse, + name + " does not exist or can't be found "); + } catch (ElementExistException eee) { + // rethrow + throw new InvocationTargetException( + "Scripting framework error renaming script ", null, eee); + } catch (WrappedTargetException wte) { + // rethrow + throw new InvocationTargetException( + "Scripting framework rename script ", null, wte.TargetException); + } + } else { + throw new com.sun.star.lang.IllegalArgumentException( + "Function " + aFunctionName + " not supported."); + } + + return result; + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + + return null; + } + + public boolean hasMethod(String aName) { + return false; + } + + public boolean hasProperty(String aName) { + return false; + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB.java b/scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB.java new file mode 100644 index 000000000..c187fb0e1 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/DeployedUnoPackagesDB.java @@ -0,0 +1,192 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.container; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.ArrayList; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class DeployedUnoPackagesDB { + + // This is the default contents of a parcel descriptor to be used when + // creating empty descriptors + private static final byte[] EMPTY_DOCUMENT = + ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<unopackages xmlns:unopackages=\"unopackages.dtd\">\n" + + "</unopackages>").getBytes(); + + private Document document = null; + + public DeployedUnoPackagesDB() throws IOException { + + ByteArrayInputStream bis = null; + + try { + bis = new ByteArrayInputStream(EMPTY_DOCUMENT); + this.document = XMLParserFactory.getParser().parse(bis); + } finally { + if (bis != null) + bis.close(); + } + } + + private DeployedUnoPackagesDB(Document document) { + this.document = document; + } + + public DeployedUnoPackagesDB(InputStream is) throws IOException { + this(XMLParserFactory.getParser().parse(is)); + } + + public String[] getDeployedPackages(String language) { + + ArrayList<String> packageUrls = new ArrayList<String>(4); + Element main = document.getDocumentElement(); + Element root = null; + int len = 0; + NodeList langNodes = null; + + if ((langNodes = main.getElementsByTagName("language")) != null && + (len = langNodes.getLength()) != 0) { + for (int i = 0; i < len; i++) { + Element e = (Element)langNodes.item(i); + + if (e.getAttribute("value").equals(language)) { + root = e; + break; + } + } + } + + if (root != null) { + len = 0; + NodeList packages = null; + + if ((packages = root.getElementsByTagName("package")) != null && + (len = packages.getLength()) != 0) { + + for (int i = 0; i < len; i++) { + Element e = (Element)packages.item(i); + packageUrls.add(e.getAttribute("value")); + } + + } + } + + if (!packageUrls.isEmpty()) { + return packageUrls.toArray(new String[packageUrls.size()]); + } + + return new String[0]; + } + + public void write(OutputStream out) throws IOException { + XMLParserFactory.getParser().write(document, out); + } + + public Document getDocument() { + return document; + } + + + public boolean removePackage(String language, String url) { + + Element main = document.getDocumentElement(); + Element langNode = null; + int len = 0; + NodeList langNodes = null; + + if ((langNodes = main.getElementsByTagName("language")) != null && + (len = langNodes.getLength()) != 0) { + for (int i = 0; i < len; i++) { + Element e = (Element)langNodes.item(i); + + if (e.getAttribute("value").equals(language)) { + langNode = e; + break; + } + } + } + + if (langNode == null) { + return false; + } + len = 0; + NodeList packages = null; + boolean result = false; + + if ((packages = langNode.getElementsByTagName("package")) != null && + (len = packages.getLength()) != 0) { + for (int i = 0; i < len; i++) { + + Element e = (Element)packages.item(i); + String value = e.getAttribute("value"); + + if (value.equals(url)) { + langNode.removeChild(e); + result = true; + break; + } + } + } + + return result; + } + + public void addPackage(String language, String url) { + + Element main = document.getDocumentElement(); + Element langNode = null; + Element pkgNode = null; + + int len = 0; + NodeList langNodes = null; + + if ((langNodes = document.getElementsByTagName("language")) != null && + (len = langNodes.getLength()) != 0) { + for (int i = 0; i < len; i++) { + Element e = (Element)langNodes.item(i); + + if (e.getAttribute("value").equals(language)) { + langNode = e; + break; + } + } + } + + if (langNode == null) { + langNode = document.createElement("language"); + langNode.setAttribute("value", language); + } + + pkgNode = document.createElement("package"); + pkgNode.setAttribute("value", url); + + langNode.appendChild(pkgNode); + //add to the Top Element + main.appendChild(langNode); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/container/Parcel.java b/scripting/java/com/sun/star/script/framework/container/Parcel.java new file mode 100644 index 000000000..6be4cad99 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/Parcel.java @@ -0,0 +1,302 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.container; + +import com.sun.star.container.ElementExistException; +import com.sun.star.container.XNameContainer; + +import com.sun.star.script.framework.io.XInputStreamImpl; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess; +import com.sun.star.ucb.XSimpleFileAccess2; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +public class Parcel implements XNameContainer { + + private ParcelDescriptor m_descriptor; + private String name; + protected ParcelContainer parent; + protected XSimpleFileAccess m_xSFA; + + public Parcel(XSimpleFileAccess xSFA, ParcelContainer parent, + ParcelDescriptor desc, String parcelName) { + + this(parent, desc, parcelName); + this.m_xSFA = xSFA; + } + + private Parcel(ParcelContainer parent, ParcelDescriptor desc, + String parcelName) { + + this.parent = parent; + this.m_descriptor = desc; + this.name = parcelName; + } + + /** + * Tests if this <tt>Parcel</tt> is in a UNO package + * or within a sub package within a UNO package + * + * @return <tt>true</tt> if has parent <tt>false</tt> otherwise + */ + public boolean isUnoPkg() { + return parent.isUnoPkg(); + } + + public String getName() { + return name; + } + + public ScriptMetaData getByName(String aName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("** Parcel.getByName for " + aName); + ScriptEntry thescript = null; + + try { + if (m_descriptor != null && hasElements()) { + ScriptEntry[] scripts = m_descriptor.getScriptEntries(); + + if (scripts.length != 0) { + for (ScriptEntry script : scripts) { + if (script.getLanguageName().equals(aName)) { + thescript = script; + break; + } + } + } + } + } + // catch unknown or un-checked exceptions + catch (Exception e) { + throw new com.sun.star.lang.WrappedTargetException(e); + } + + if (thescript == null) { + LogUtils.DEBUG("No script for " + aName); + throw new com.sun.star.container.NoSuchElementException("No script named " + + aName); + } + + ScriptMetaData data = new ScriptMetaData(this, thescript, null); + + LogUtils.DEBUG("returning date for " + aName); + return data; + } + + public String[] getElementNames() { + + String[] results = new String[0]; + + if (m_descriptor != null) { + ScriptEntry[] scripts = m_descriptor.getScriptEntries(); + results = new String[ scripts.length ]; + + for (int index = 0; index < scripts.length; index++) { + results[ index ] = scripts[ index ].getLanguageName(); + } + } + + return results; + } + + public boolean hasByName(String aName) { + + boolean result = true; + ScriptMetaData containee = null; + + try { + containee = getByName(aName); + + if (containee != null) { + result = true; + } + } catch (Exception e) { + result = false; + } + + return result; + } + + public com.sun.star.uno.Type getElementType() { + // TODO at the moment this returns void indicating + // type is unknown ( from UNO point of view this is correct ) + // but, maybe we want to have a private UNO interface + + return new Type(); + } + + public boolean hasElements() { + return m_descriptor != null && m_descriptor.getScriptEntries().length > 0; + } + + public void replaceByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + // TODO check type of aElement + // if not ok, throw IllegalArgument + if (m_descriptor != null) { + try { + ScriptMetaData script = getByName(aName); + + if (script != null) { + //m_descriptor.removeScriptEntry( script ); + // TODO needs to create source file ( if there is one ) + //m_descriptor.write(); + } else { + throw new com.sun.star.container.NoSuchElementException( + "No script named " + aName); + } + + } + // TO DO should catch specified exceptions + catch (Exception ex) { + throw new com.sun.star.lang.WrappedTargetException(ex); + } + + } + } + + // create + public void insertByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, ElementExistException, + com.sun.star.lang.WrappedTargetException { + + // TODO check the type of aElement and throw# + // if not appropriate + try { + if (hasByName(aName)) { + throw new ElementExistException(aName); + } + + ScriptMetaData script = (ScriptMetaData)aElement; + + if (script.hasSource()) { + LogUtils.DEBUG("Inserting source: " + script.getSource()); + + if (!script.writeSourceFile()) { + throw new com.sun.star.lang.WrappedTargetException( + "Failed to create source file " + script.getLanguageName()); + } + } + + m_descriptor.addScriptEntry(script); + writeParcelDescriptor(); + } catch (Exception e) { + LogUtils.DEBUG("Failed to insert entry " + aName + ": " + e.getMessage()); + throw new com.sun.star.lang.WrappedTargetException(e); + } + } + + private void writeParcelDescriptor() + throws com.sun.star.ucb.CommandAbortedException, + com.sun.star.io.IOException, + com.sun.star.uno.Exception, java.io.IOException { + + String pathToDescriptor = + PathUtils.make_url(getPathToParcel(), ParcelDescriptor.PARCEL_DESCRIPTOR_NAME); + + XSimpleFileAccess2 xSFA2 = + UnoRuntime.queryInterface(XSimpleFileAccess2.class, m_xSFA); + + if (xSFA2 != null) { + + ByteArrayOutputStream bos = null; + ByteArrayInputStream bis = null; + XInputStreamImpl xis = null; + + try { + bos = new ByteArrayOutputStream(1024); + m_descriptor.write(bos); + bis = new ByteArrayInputStream(bos.toByteArray()); + + xis = new XInputStreamImpl(bis); + xSFA2.writeFile(pathToDescriptor, xis); + } finally { + if (bos != null) bos.close(); + + if (bis != null) bis.close(); + + if (xis != null) xis.closeInput(); + } + } + } + + // delete + public void removeByName(String Name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + try { + ScriptMetaData script = getByName(Name); + + if (script != null) { + if (!script.removeSourceFile()) { + LogUtils.DEBUG("** Parcel.removeByName Failed to remove script " + + Name); + throw new com.sun.star.lang.WrappedTargetException( + "Failed to remove script " + Name); + } + + LogUtils.DEBUG("** Parcel.removeByName have removed script source file " + + Name); + + m_descriptor.removeScriptEntry(script); + writeParcelDescriptor(); + } else { + throw new com.sun.star.container.NoSuchElementException( + "No script named " + Name); + } + + } catch (Exception e) { + LogUtils.DEBUG("** Parcel.removeByName Exception: " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + } + + // rename parcel + public void rename(String name) { + this.name = name; + } + + public ParcelContainer getParent() { + return parent; + } + + /** + * Returns the path of this <tt>Parcel</tt> + * + * @return <tt>String</tt> path to parcel + */ + public String getPathToParcel() { + String path = parent.getParcelContainerDir() + "/" + name; + return path; + } + +} diff --git a/scripting/java/com/sun/star/script/framework/container/ParcelContainer.java b/scripting/java/com/sun/star/script/framework/container/ParcelContainer.java new file mode 100644 index 000000000..4a2bece39 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ParcelContainer.java @@ -0,0 +1,725 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.container; + +import com.sun.star.container.ElementExistException; +import com.sun.star.container.XNameAccess; +import com.sun.star.container.XNameContainer; + +import com.sun.star.io.XInputStream; + +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.script.framework.io.XInputStreamImpl; +import com.sun.star.script.framework.io.XInputStreamWrapper; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess; +import com.sun.star.ucb.XSimpleFileAccess2; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import com.sun.star.uri.XUriReference; +import com.sun.star.uri.XUriReferenceFactory; +import com.sun.star.uri.XVndSunStarScriptUrl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.StringTokenizer; + +/** + * The <code>ParcelContainer</code> object is used to store the + * ScriptingFramework specific Libraries. + */ +public class ParcelContainer implements XNameAccess { + + protected static XSimpleFileAccess m_xSFA; + + protected String language; + protected String containerUrl; + private Collection<Parcel> parcels = new ArrayList<Parcel>(10); + protected XComponentContext m_xCtx; + private ParcelContainer parent = null; + private final Collection<ParcelContainer> childContainers = new ArrayList<ParcelContainer>(10); + private boolean isPkgContainer = false; + + /** + * Tests if this <tt>ParcelContainer</tt> represents a UNO package + * or sub package within a UNO package + * + * @return <tt>true</tt> if has parent <tt>false</tt> otherwise + */ + public boolean isUnoPkg() { + return isPkgContainer; + } + + /** + * Returns this <tt>ParcelContainer</tt>'s parent + * + * @return <tt>ParcelContainer</tt> if has parent null otherwise + */ + public ParcelContainer parent() { + return parent; + } + + /** + * Returns all child <tt>ParcelContainer</tt> + * this instance of <tt>ParcelContainer</tt> + * + * @return a new array of ParcelContainers. A zero + * length array is returned if no child ParcelContainers. + */ + public ParcelContainer[] getChildContainers() { + if (childContainers.isEmpty()) { + return new ParcelContainer[0]; + } + + return childContainers.toArray(new ParcelContainer[childContainers.size()]); + } + + /** + * Removes a child <tt>ParcelContainer</tt> + * from this instance. + * @param child <tt>ParcelContainer</tt> to be added. + * + * @return <tt>true</tt> if child successfully removed + */ + public boolean removeChildContainer(ParcelContainer child) { + return childContainers.remove(child); + } + + /** + * Adds a new child <tt>ParcelContainer</tt> + * to this instance. + * @param child <tt>ParcelContainer</tt> to be added. + * + */ + public void addChildContainer(ParcelContainer child) { + childContainers.add(child); + } + + /** + * Returns a child <tt>ParcelContainer</tt> whose location + * matches the <tt>location</tt> argument passed to this method. + * @param key the <tt>location</tt> which is to + * be matched. + * + * @return child <tt>ParcelContainer</tt> or {@code null} if none + * found. + */ + public ParcelContainer getChildContainer(String key) { + ParcelContainer result = null; + + for (ParcelContainer c : childContainers) { + + String name = c.getName(); + if (name == null) + { + continue; + } + + String location = + ScriptMetaData.getLocationPlaceHolder(c.containerUrl, name); + + if (key.equals(location)) { + result = c; + break; + } + } + + return result; + } + + /** + * Returns a child <tt>ParcelContainer</tt> whose member + * <tt>containerUrl</tt> matches the <tt>containerUrl</tt> + * argument passed to this method. + * @param containerUrl the <tt>containerUrl</tt> which is to + * be matched. + * + * @return child <tt>ParcelContainer</tt> or {@code null} if none + * found. + */ + public ParcelContainer getChildContainerForURL(String containerUrl) { + ParcelContainer result = null; + + for (ParcelContainer c : childContainers) { + if (containerUrl.equals(c.containerUrl)) { + result = c; + break; + } + } + + return result; + } + + /** + * Returns Name of this container. Name of this <tt>ParcelContainer</tt> + * is determined from the <tt>containerUrl</tt> as the last portion + * of the URL after the last forward slash. + * @return name of <tt>ParcelContainer</tt> + * found. + */ + public String getName() { + String name = null; + + // TODO handler package ParcelContainer? + if (!containerUrl.startsWith("vnd.sun.star.tdoc:")) { + try { + // return name + String decodedUrl = java.net.URLDecoder.decode(containerUrl, "UTF-8"); + int indexOfSlash = decodedUrl.lastIndexOf('/'); + + if (indexOfSlash != -1) { + name = decodedUrl.substring(indexOfSlash + 1); + } + } catch (UnsupportedEncodingException e) { + throw new com.sun.star.uno.RuntimeException(e); + } + } else { + name = "document"; + } + + return name; + } + + /** + * Initializes a newly created <code>ParcelContainer</code> object. + * @param xCtx UNO component context + * @param containerUrl location of this container. + * @param language language for which entries are stored + */ + public ParcelContainer(XComponentContext xCtx, String containerUrl, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + this(null, xCtx, containerUrl, language, true); + } + + /** + * Initializes a newly created <code>ParcelContainer</code> object. + * @param xCtx UNO component context + * @param containerUrl location of this container. + * @param language language for which entries are stored + * @param loadParcels set to <tt>true</tt> if parcels are to be loaded + * on construction. + */ + public ParcelContainer(XComponentContext xCtx, String containerUrl, + String language, boolean loadParcels) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + this(null, xCtx, containerUrl, language, loadParcels); + } + + /** + * Initializes a newly created <code>ParcelContainer</code> object. + * @param parent parent ParcelContainer + * @param xCtx UNO component context + * @param containerUrl location of this container. + * @param language language for which entries are stored + * @param loadParcels set to <tt>true</tt> if parcels are to be loaded + * on construction. + */ + public ParcelContainer(ParcelContainer parent, XComponentContext xCtx, + String containerUrl, String language, + boolean loadParcels) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("Creating ParcelContainer for " + containerUrl + + " loadParcels = " + loadParcels + " language = " + language); + + this.m_xCtx = xCtx; + this.language = language; + this.parent = parent; + this.containerUrl = containerUrl; + + initSimpleFileAccess(); + boolean parentIsPkgContainer = false; + + if (parent != null) { + parentIsPkgContainer = parent.isUnoPkg(); + } + + if (containerUrl.endsWith("uno_packages") || parentIsPkgContainer) { + isPkgContainer = true; + } + + if (loadParcels) { + loadParcels(); + } + } + + + public String getContainerURL() { + return this.containerUrl; + } + + private void initSimpleFileAccess() { + synchronized (ParcelContainer.class) { + if (m_xSFA != null) { + return; + } + + try { + + m_xSFA = UnoRuntime.queryInterface( + XSimpleFileAccess.class, + m_xCtx.getServiceManager().createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", m_xCtx)); + + } catch (Exception e) { + // TODO should throw + LogUtils.DEBUG("Error instantiating simplefile access "); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + } + + public String getParcelContainerDir() { + // If this container does not represent a uno-package + // then it is a document, user or share + // in each case the convention is to have a Scripts/[language] + // dir where scripts reside + if (!isUnoPkg()) { + return PathUtils.make_url(containerUrl , "Scripts/" + language.toLowerCase()); + } + + return containerUrl; + } + + public Object getByName(String aName) throws + com.sun.star.container.NoSuchElementException, WrappedTargetException { + + Parcel parcel = null; + + try { + if (hasElements()) { + for (Parcel parcelToCheck : parcels) { + if (parcelToCheck.getName().equals(aName)) { + parcel = parcelToCheck; + break; + } + } + } + } catch (Exception e) { + throw new WrappedTargetException(e); + } + + if (parcel == null) { + throw new com.sun.star.container.NoSuchElementException("Macro Library " + aName + + " not found"); + } + + return parcel; + } + + public String[] getElementNames() { + + if (hasElements()) { + + Parcel[] theParcels = parcels.toArray(new Parcel[parcels.size()]); + String[] names = new String[ theParcels.length ]; + + for (int count = 0; count < names.length; count++) { + names[count] = theParcels[ count ].getName(); + } + + return names; + } + + return new String[0]; + } + + public boolean hasByName(String aName) { + + boolean isFound = false; + + try { + if (getByName(aName) != null) { + isFound = true; + } + + } catch (Exception e) { + //TODO - handle trace + } + + return isFound; + } + + public Type getElementType() { + return new Type(); + } + + public boolean hasElements() { + return !(parcels == null || parcels.isEmpty()); + } + + private void loadParcels() throws com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + try { + LogUtils.DEBUG("About to load parcels from " + containerUrl); + + if (m_xSFA.isFolder(getParcelContainerDir())) { + LogUtils.DEBUG(getParcelContainerDir() + " is a folder "); + String[] children = m_xSFA.getFolderContents(getParcelContainerDir(), true); + parcels = new ArrayList<Parcel>(children.length); + + for (String child : children) { + LogUtils.DEBUG("Processing " + child); + + try { + loadParcel(child); + } catch (java.lang.Exception e) { + // print an error message and move on to + // the next parcel + LogUtils.DEBUG("ParcelContainer.loadParcels caught " + e.getClass().getName() + + " exception loading parcel " + child + ": " + e.getMessage()); + } + } + } else { + LogUtils.DEBUG(" ParcelCOntainer.loadParcels " + getParcelContainerDir() + + " is not a folder "); + } + + } catch (com.sun.star.ucb.CommandAbortedException e) { + LogUtils.DEBUG("ParcelContainer.loadParcels caught exception processing folders for " + + getParcelContainerDir()); + LogUtils.DEBUG("TRACE: " + LogUtils.getTrace(e)); + throw new com.sun.star.lang.WrappedTargetException(e); + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("ParcelContainer.loadParcels caught exception processing folders for " + + getParcelContainerDir()); + LogUtils.DEBUG("TRACE: " + LogUtils.getTrace(e)); + throw new com.sun.star.lang.WrappedTargetException(e); + } + } + + public XNameContainer createParcel(String name) throws + ElementExistException, com.sun.star.lang.WrappedTargetException { + + Parcel p = null; + + if (hasByName(name)) { + throw new ElementExistException("Parcel " + name + " already exists"); + } + + String pathToParcel = PathUtils.make_url(getParcelContainerDir(), name); + + try { + LogUtils.DEBUG("ParcelContainer.createParcel, creating folder " + + pathToParcel); + + m_xSFA.createFolder(pathToParcel); + + LogUtils.DEBUG("ParcelContainer.createParcel, folder " + pathToParcel + + " created "); + + ParcelDescriptor pd = new ParcelDescriptor(); + pd.setLanguage(language); + + String parcelDesc = + PathUtils.make_url(pathToParcel, ParcelDescriptor.PARCEL_DESCRIPTOR_NAME); + + XSimpleFileAccess2 xSFA2 = + UnoRuntime.queryInterface(XSimpleFileAccess2.class, m_xSFA); + + if (xSFA2 != null) { + LogUtils.DEBUG("createParcel() Using XSIMPLEFILEACCESS2 " + parcelDesc); + ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); + pd.write(bos); + bos.close(); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + XInputStreamImpl xis = new XInputStreamImpl(bis); + xSFA2.writeFile(parcelDesc, xis); + xis.closeInput(); + p = loadParcel(pathToParcel); + } + } catch (Exception e) { + LogUtils.DEBUG("createParcel() Exception while attempting to create = " + + name); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + return p; + } + + public Parcel loadParcel(String parcelUrl) throws + com.sun.star.lang.WrappedTargetException, + com.sun.star.lang.IllegalArgumentException { + + String parcelDescUrl = + PathUtils.make_url(parcelUrl, ParcelDescriptor.PARCEL_DESCRIPTOR_NAME); + + Parcel parcel = null; + XInputStream xis = null; + InputStream is = null; + + try { + if (m_xSFA.exists(parcelDescUrl)) { + + LogUtils.DEBUG("ParcelContainer.loadParcel opening " + parcelDescUrl); + + xis = m_xSFA.openFileRead(parcelDescUrl); + is = new XInputStreamWrapper(xis); + ParcelDescriptor pd = new ParcelDescriptor(is) ; + + try { + is.close(); + is = null; + } catch (Exception e) { + LogUtils.DEBUG( + "ParcelContainer.loadParcel Exception when closing stream for " + + parcelDescUrl + " :" + e); + } + + LogUtils.DEBUG("ParcelContainer.loadParcel closed " + parcelDescUrl); + + if (!pd.getLanguage().equals(language)) { + LogUtils.DEBUG("ParcelContainer.loadParcel Language of Parcel does not match this container "); + return null; + } + + LogUtils.DEBUG("Processing " + parcelDescUrl + " closed "); + + int indexOfSlash = parcelUrl.lastIndexOf('/'); + String name = parcelUrl.substring(indexOfSlash + 1); + + parcel = new Parcel(m_xSFA, this, pd, name); + + LogUtils.DEBUG(" ParcelContainer.loadParcel created parcel for " + + parcelDescUrl + " for language " + language); + + parcels.add(parcel); + } else { + throw new java.io.IOException(parcelDescUrl + " does NOT exist!"); + } + } catch (com.sun.star.ucb.CommandAbortedException e) { + LogUtils.DEBUG("loadParcel() Exception while accessing filesystem url = " + + parcelDescUrl + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } catch (java.io.IOException e) { + LogUtils.DEBUG("ParcelContainer.loadParcel() caught IOException while accessing " + + parcelDescUrl + ": " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("loadParcel() Exception while accessing filesystem url = " + + parcelDescUrl + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + finally { + if (is != null) { + try { + is.close(); // is will close xis + } catch (Exception ignore) { + } + } else if (xis != null) { + try { + xis.closeInput(); + } catch (Exception ignore) { + } + } + } + + return parcel; + } + public void renameParcel(String oldName, String newName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG(" ** ParcelContainer Renaming parcel " + oldName + " to " + + newName); + LogUtils.DEBUG(" ** ParcelContainer is " + this); + + Parcel p = (Parcel)getByName(oldName); + + if (p == null) { + throw new com.sun.star.container.NoSuchElementException( + "No parcel named " + oldName); + } + + String oldParcelDirUrl = + PathUtils.make_url(getParcelContainerDir(), oldName); + + String newParcelDirUrl = + PathUtils.make_url(getParcelContainerDir(), newName); + + try { + if (!m_xSFA.isFolder(oldParcelDirUrl)) { + Exception e = new com.sun.star.io.IOException( + "Invalid Parcel directory: " + oldName); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + LogUtils.DEBUG(" ** ParcelContainer Renaming folder " + oldParcelDirUrl + + " to " + newParcelDirUrl); + + m_xSFA.move(oldParcelDirUrl, newParcelDirUrl); + + } catch (com.sun.star.ucb.CommandAbortedException ce) { + LogUtils.DEBUG(" ** ParcelContainer Renaming failed with " + ce); + throw new com.sun.star.lang.WrappedTargetException(ce); + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG(" ** ParcelContainer Renaming failed with " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + p.rename(newName); + } + // removes but doesn't physically delete parcel from container + public boolean removeParcel(String name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + Parcel p = (Parcel)getByName(name); + + if (p == null) { + throw new com.sun.star.container.NoSuchElementException( + "No parcel named " + name); + } + + return parcels.remove(p); + } + + public boolean deleteParcel(String name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("deleteParcel for containerURL " + containerUrl + + " name = " + name + " Language = " + language); + + Parcel p = (Parcel)getByName(name); + + if (p == null) { + throw new com.sun.star.container.NoSuchElementException( + "No parcel named " + name); + } + + try { + String pathToParcel = PathUtils.make_url(getParcelContainerDir(), name); + m_xSFA.kill(pathToParcel); + } catch (Exception e) { + LogUtils.DEBUG("Error deleting parcel " + name); + throw new com.sun.star.lang.WrappedTargetException(e); + } + + return parcels.remove(p); + } + + public String getLanguage() { + return language; + } + + public ScriptMetaData findScript(ParsedScriptUri parsedUri) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + Parcel p = (Parcel)getByName(parsedUri.parcel); + ScriptMetaData scriptData = p.getByName(parsedUri.function); + + LogUtils.DEBUG("** found script data for " + parsedUri.function + " script is " + + scriptData); + + return scriptData; + } + + public ParsedScriptUri parseScriptUri(String scriptURI) throws + com.sun.star.lang.IllegalArgumentException { + + XMultiComponentFactory xMcFac = null; + XUriReferenceFactory xFac = null; + + try { + xMcFac = m_xCtx.getServiceManager(); + + xFac = UnoRuntime.queryInterface( + XUriReferenceFactory.class, xMcFac.createInstanceWithContext( + "com.sun.star.uri.UriReferenceFactory", m_xCtx)); + + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("Problems parsing URL:" + e.toString()); + throw new com.sun.star.lang.IllegalArgumentException( + e, "Problems parsing URL"); + } + + if (xFac == null) { + LogUtils.DEBUG("Failed to create UrlReference factory"); + throw new com.sun.star.lang.IllegalArgumentException( + "Failed to create UrlReference factory for url " + scriptURI); + } + + XUriReference uriRef = xFac.parse(scriptURI); + + XVndSunStarScriptUrl sfUri = + UnoRuntime.queryInterface(XVndSunStarScriptUrl.class, uriRef); + + if (sfUri == null) { + LogUtils.DEBUG("Failed to parse url"); + throw new com.sun.star.lang.IllegalArgumentException( + "Failed to parse url " + scriptURI); + } + + ParsedScriptUri parsedUri = new ParsedScriptUri(); + parsedUri.function = sfUri.getName(); + parsedUri.parcel = ""; + + // parse parcel name; + StringTokenizer tokenizer = new StringTokenizer(parsedUri.function, "."); + + if (tokenizer.hasMoreElements()) { + parsedUri.parcel = (String)tokenizer.nextElement(); + LogUtils.DEBUG("** parcelName = " + parsedUri.parcel); + } + + if (parsedUri.function.length() > 0) { + + // strip out parcel name + parsedUri.function = + parsedUri.function.substring(parsedUri.parcel.length() + 1); + + } + + // parse location + parsedUri.location = sfUri.getParameter("location"); + + // TODO basic sanity check on language, location, function name, parcel + // should be correct e.g. verified by MSP and LangProvider by the + // time it's got to here + + LogUtils.DEBUG("** location = " + parsedUri.location + + "\nfunction = " + parsedUri.function + + "\nparcel = " + parsedUri.parcel + + "\nlocation = " + parsedUri.location); + + return parsedUri; + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/ParcelDescriptor.java b/scripting/java/com/sun/star/script/framework/container/ParcelDescriptor.java new file mode 100644 index 000000000..eeb8f9ef2 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ParcelDescriptor.java @@ -0,0 +1,354 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.container; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.w3c.dom.CharacterData; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class ParcelDescriptor { + + // File name to be used for parcel descriptor files + public static final String + PARCEL_DESCRIPTOR_NAME = "parcel-descriptor.xml"; + + // This is the default contents of a parcel descriptor to be used when + // creating empty descriptors + private static final byte[] EMPTY_DOCUMENT = + ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<parcel xmlns:parcel=\"scripting.dtd\" language=\"Java\">\n" + + "</parcel>").getBytes(); + + private Document document = null; + private String language = null; + private final Map<String, String> languagedepprops = new HashMap<String, String>(3); + + public ParcelDescriptor() throws IOException { + ByteArrayInputStream bis = null; + + try { + bis = new ByteArrayInputStream(EMPTY_DOCUMENT); + this.document = XMLParserFactory.getParser().parse(bis); + } finally { + if (bis != null) + bis.close(); + } + } + + private ParcelDescriptor(Document document) { + this.document = document; + initLanguageProperties(); + } + + public ParcelDescriptor(InputStream is) throws IOException { + this(XMLParserFactory.getParser().parse(is)); + } + + public ParcelDescriptor(File file) throws IOException { + this(file, "Java"); + } + + private ParcelDescriptor(File file, String language) throws IOException { + if (file.exists()) { + FileInputStream fis = null; + + try { + fis = new FileInputStream(file); + this.document = XMLParserFactory.getParser().parse(fis); + } finally { + if (fis != null) + fis.close(); + } + } else { + ByteArrayInputStream bis = null; + + try { + bis = new ByteArrayInputStream(EMPTY_DOCUMENT); + this.document = XMLParserFactory.getParser().parse(bis); + } finally { + if (bis != null) + bis.close(); + } + + setLanguage(language); + } + + initLanguageProperties(); + } + + public void write(OutputStream out) throws IOException { + XMLParserFactory.getParser().write(document, out); + } + + public Document getDocument() { + return document; + } + + public String getLanguage() { + if (language == null && document != null) { + Element e = document.getDocumentElement(); + language = e.getAttribute("language"); + } + + return language; + } + + public void setLanguage(String language) { + this.language = language; + + if (document != null) { + try { + Element e = document.getDocumentElement(); + e.setAttribute("language", language); + } catch (DOMException de) { + } + } + } + + public ScriptEntry[] getScriptEntries() { + + ArrayList<ScriptEntry> scripts = new ArrayList<ScriptEntry>(); + NodeList scriptNodes; + int len; + + if (document == null || + (scriptNodes = document.getElementsByTagName("script")) == null || + (len = scriptNodes.getLength()) == 0) + return new ScriptEntry[0]; + + for (int i = 0; i < len; i++) { + String language, languagename, description = ""; + Map<String, String> langProps = new HashMap<String, String>(); + NodeList nl; + + Element scriptElement = (Element)scriptNodes.item(i); + language = scriptElement.getAttribute("language"); + + // get the text of the description element + nl = scriptElement.getElementsByTagName("locale"); + + if (nl != null) { + nl = nl.item(0).getChildNodes(); + + if (nl != null) { + for (int j = 0 ; j < nl.getLength(); j++) { + if (nl.item(j).getNodeName().equals("description")) { + CharacterData cd = + (CharacterData)nl.item(j).getFirstChild(); + description = cd.getData().trim(); + } + } + } + } + + nl = scriptElement.getElementsByTagName("functionname"); + + if (nl == null) { + languagename = ""; + } else { + Element tmp = (Element)nl.item(0); + languagename = tmp.getAttribute("value"); + } + + nl = scriptElement.getElementsByTagName("languagedepprops"); + + if (nl != null && nl.getLength() > 0) { + + NodeList props = ((Element)nl.item(0)).getElementsByTagName("prop"); + + if (props != null) { + for (int j = 0; j < props.getLength(); j++) { + Element tmp = (Element)props.item(j); + String key = tmp.getAttribute("name"); + String val = tmp.getAttribute("value"); + langProps.put(key, val); + } + } + } + + ScriptEntry entry = + new ScriptEntry(language, languagename, langProps, description); + scripts.add(entry); + } + + return scripts.toArray(new ScriptEntry[scripts.size()]); + } + + public void setScriptEntries(ScriptEntry[] scripts) { + clearEntries(); + + for (ScriptEntry script : scripts) { + addScriptEntry(script); + } + } + + public void setScriptEntries(Iterator<ScriptEntry> scripts) { + clearEntries(); + + while (scripts.hasNext()) { + addScriptEntry(scripts.next()); + } + } + + private String getLanguageProperty(String name) { + return languagedepprops.get(name); + } + + + + private void initLanguageProperties() { + NodeList nl = document.getElementsByTagName("languagedepprops"); + int len; + + if (nl != null && (len = nl.getLength()) != 0) { + + for (int i = 0; i < len; i++) { + Element e = (Element)nl.item(i); + NodeList nl2 = e.getElementsByTagName("prop"); + int len2; + + if (nl2 != null && (len2 = nl2.getLength()) != 0) { + for (int j = 0; j < len2; j++) { + Element e2 = (Element)nl2.item(j); + + String name = e2.getAttribute("name"); + String value = e2.getAttribute("value"); + + if (getLanguageProperty(name) == null) { + languagedepprops.put(name, value); + } + } + } + } + } + } + + private void clearEntries() { + NodeList scriptNodes; + Element main = document.getDocumentElement(); + int len; + + if ((scriptNodes = document.getElementsByTagName("script")) != null && + (len = scriptNodes.getLength()) != 0) { + for (int i = len - 1; i >= 0; i--) { + try { + main.removeChild(scriptNodes.item(i)); + } catch (DOMException de) { + // ignore + } + } + } + } + + public void removeScriptEntry(ScriptEntry script) { + NodeList scriptNodes; + Element main = document.getDocumentElement(); + int len; + + if ((scriptNodes = document.getElementsByTagName("script")) != null && + (len = scriptNodes.getLength()) != 0) { + for (int i = len - 1; i >= 0; i--) { + try { + Element scriptElement = (Element)scriptNodes.item(i); + String languagename = ""; + + NodeList nl = + scriptElement.getElementsByTagName("functionname"); + + if (nl == null) { + continue; + } else { + Element tmp = (Element)nl.item(0); + languagename = tmp.getAttribute("value"); + } + + if (languagename.equals(script.getLanguageName())) { + main.removeChild(scriptElement); + } + } catch (DOMException de) { + // ignore + } + } + } + } + + public void addScriptEntry(ScriptEntry script) { + Element main = document.getDocumentElement(); + Element root, item, tempitem; + + root = document.createElement("script"); + root.setAttribute("language", script.getLanguage()); + + item = document.createElement("locale"); + item.setAttribute("lang", "en"); + tempitem = document.createElement("displayname"); + tempitem.setAttribute("value", script.getLogicalName()); + item.appendChild(tempitem); + + tempitem = document.createElement("description"); + String description = script.getDescription(); + + if (description == null || description.length() == 0) { + description = script.getLogicalName(); + } + + tempitem.appendChild(document.createTextNode(description)); + item.appendChild(tempitem); + + root.appendChild(item); + + item = document.createElement("logicalname"); + item.setAttribute("value", script.getLogicalName()); + root.appendChild(item); + + item = document.createElement("functionname"); + item.setAttribute("value", script.getLanguageName()); + root.appendChild(item); + + if (languagedepprops != null && !languagedepprops.isEmpty()) { + String key; + item = document.createElement("languagedepprops"); + + for (Map.Entry<String, String> entry : languagedepprops.entrySet()) { + tempitem = document.createElement("prop"); + tempitem.setAttribute("name", entry.getKey()); + tempitem.setAttribute("value", entry.getValue()); + item.appendChild(tempitem); + } + + root.appendChild(item); + } + + //add to the Top Element + main.appendChild(root); + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/ParsedScriptUri.java b/scripting/java/com/sun/star/script/framework/container/ParsedScriptUri.java new file mode 100644 index 000000000..796314f00 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ParsedScriptUri.java @@ -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 . + */ +package com.sun.star.script.framework.container; + +public class ParsedScriptUri { + + public String location; + public String function; + public String parcel; +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/container/ScriptEntry.java b/scripting/java/com/sun/star/script/framework/container/ScriptEntry.java new file mode 100644 index 000000000..92870c680 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ScriptEntry.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.container; + +import java.util.HashMap; +import java.util.Map; + +public class ScriptEntry { + + private final String language; + private final String languagename; + private final String logicalname; + private final String description; + + private final Map<String, String> languagedepprops; + + protected ScriptEntry(ScriptEntry entry) { + this.language = entry.language; + this.languagename = entry.languagename; + this.logicalname = entry.languagename; + this.languagedepprops = entry.languagedepprops; + this.description = entry.description; + } + + public ScriptEntry(String language, String languagename) { + this(language, languagename, new HashMap<String, String>(), ""); + } + + public ScriptEntry(String language, String languagename, + Map<String, String> languagedepprops, + String description) { + this.language = language; + this.languagename = languagename; + // logical name/ function name concept + // needs to be reworked, in meantime + // function name ( from xml ) will be used + // as logical name also + this.logicalname = languagename; + this.languagedepprops = languagedepprops; + this.description = description; + } + + public Map<String, String> getLanguageProperties() { + return languagedepprops; + } + + public String getLanguageName() { + return languagename; + } + + public String getLogicalName() { + return logicalname; + } + + public String getLanguage() { + return language; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return "\nLogicalName = " + logicalname + + "\nLanguageName = " + languagename + + "\nLanguaguageProperties = " + languagedepprops; + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/ScriptMetaData.java b/scripting/java/com/sun/star/script/framework/container/ScriptMetaData.java new file mode 100644 index 000000000..de51b1247 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/ScriptMetaData.java @@ -0,0 +1,328 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.container; + +import com.sun.star.script.framework.io.UCBStreamHandler; +import com.sun.star.script.framework.io.XInputStreamImpl; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess2; + +import com.sun.star.uno.UnoRuntime; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import java.net.URL; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +public class ScriptMetaData extends ScriptEntry { + + private boolean hasSource = false; + private static final String locationPlaceHolder = ""; + private String source; + private final Parcel parent; + + + public ScriptMetaData(Parcel parent, ScriptEntry entry, + String source) { + super(entry); + this.parent = parent; + + if (source != null) { + this.hasSource = true; + this.source = source; + } + } + + public boolean hasSource() { + return hasSource; + } + + public String getSource() { + return (source != null && hasSource) ? source : null; + } + + public byte[] getSourceBytes() { + return (source != null && hasSource) ? source.getBytes() : null; + } + + public String getScriptFullURL() { + String url = "vnd.sun.star.script:" + parent.getName() + "." + + getLanguageName() + "?" + "language=" + getLanguage() + + "&location=" + getParcelLocation(); + + return url; + } + + public String getShortFormScriptURL() { + String url = "vnd.sun.star.script:" + parent.getName() + "." + + getLanguageName() + "?" + "language=" + getLanguage() + + "&location=" + getLocationPlaceHolder(); + + return url; + } + + // TODO probably should be private should not be necessary + // to be exposed at all + + private static final String SHARE = + "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR"; + + private static final String USER = + "vnd.sun.star.expand:${$BRAND_INI_DIR/" + PathUtils.BOOTSTRAP_NAME + + "::UserInstallation}/user"; + + private static final String UNO_USER_PACKAGES1 = + "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE"; + + private static final String UNO_USER_PACKAGES2 = USER + "/uno_packages"; + + private static final String UNO_SHARED_PACKAGES1 = + "$UNO_SHARED_PACKAGES_CACHE"; + + private static final String UNO_SHARED_PACKAGES2 = SHARE + "/uno_packages"; + + public static String getFileName(URL url) { + String fileName = url.toExternalForm(); + if (fileName.lastIndexOf(UCBStreamHandler.separator) != -1) { + fileName = fileName.substring(0, fileName.lastIndexOf(UCBStreamHandler.separator)); + fileName = fileName.substring(fileName.lastIndexOf("/") + 1); + } + return fileName; + } + + public static String getLocationPlaceHolder(String url, String pkgname) { + String result = "Unknown"; + + if (url.contains(UNO_USER_PACKAGES1) || + url.contains(UNO_USER_PACKAGES2)) { + result = PathUtils.make_url("user:uno_packages", pkgname); + } else if (url.contains(UNO_SHARED_PACKAGES1) || + url.contains(UNO_SHARED_PACKAGES2)) { + result = PathUtils.make_url("share:uno_packages", pkgname); + } else if (url.indexOf(SHARE) == 0) { + result = "share"; + } else if (url.indexOf(USER) == 0) { + result = "user"; + } else if (url.indexOf("vnd.sun.star.tdoc:") == 0) { + result = "document"; + } + + return result; + } + + public String getLocationPlaceHolder() { + String placeHolder = "Unknown"; + String pathToParcel = parent.getPathToParcel(); + + if (pathToParcel.contains(UNO_USER_PACKAGES1) || + pathToParcel.contains(UNO_USER_PACKAGES2)) { + // it's a package + placeHolder = "user:uno_packages"; + String unoPkg = parent.parent.getName(); + + if (unoPkg != null) { + placeHolder = PathUtils.make_url(placeHolder, unoPkg); + } + } else if (pathToParcel.contains(UNO_SHARED_PACKAGES1) || + pathToParcel.contains(UNO_SHARED_PACKAGES2)) { + //it's a package + placeHolder = "share:uno_packages"; + String unoPkg = parent.parent.getName(); + + if (unoPkg != null) { + placeHolder = PathUtils.make_url(placeHolder, unoPkg); + } + } else if (pathToParcel.indexOf(SHARE) == 0) { + placeHolder = "share"; + } else if (pathToParcel.indexOf(USER) == 0) { + placeHolder = "user"; + } else if (pathToParcel.indexOf("vnd.sun.star.tdoc:") == 0) { + placeHolder = "document"; + } + + // TODO handling document packages ??? not really sure of package url + /* else + { + } */ + return placeHolder; + } + + // TODO probably should be private should not be necessary + // to be exposed at all only used in lang providers at the moment + // to generate URL for script, editors should use a model of script + // source and not interact with the URL + // Also if it is to remain needs to be renamed to getParcelLocationURL + + // return URL string to parcel + public String getParcelLocation() { + return parent.getPathToParcel(); + } + + @Override + public String toString() { + return "\nParcelLocation = " + getParcelLocation() + "\nLocationPlaceHolder = " + + locationPlaceHolder + super.toString(); + } + + public URL[] getClassPath() { + try { + String classpath = getLanguageProperties().get("classpath"); + + if (classpath == null) { + classpath = ""; + } + + String parcelPath = getParcelLocation(); + + // make sure path ends with / + if (!parcelPath.endsWith("/")) { + parcelPath += "/"; + } + + // replace \ with / + parcelPath = parcelPath.replace('\\', '/'); + + ArrayList<URL> classPathVec = new ArrayList<URL>(); + StringTokenizer stk = new StringTokenizer(classpath, ":"); + + while (stk.hasMoreElements()) { + String relativeClasspath = stk.nextToken(); + String pathToProcess = PathUtils.make_url(parcelPath, relativeClasspath); + URL url = createURL(pathToProcess); + + if (url != null) { + classPathVec.add(url); + } + + } + + if (classPathVec.isEmpty()) { + URL url = createURL(parcelPath); + + if (url != null) { + classPathVec.add(url); + } + } + + return classPathVec.toArray(new URL[classPathVec.size()]); + } catch (Exception e) { + LogUtils.DEBUG("Failed to build class path " + e.toString()); + LogUtils.DEBUG(LogUtils.getTrace(e)); + return new URL[0]; + } + } + + private URL createURL(String path) throws java.net.MalformedURLException { + int indexOfColon = path.indexOf(':'); + String scheme = path.substring(0, indexOfColon); + UCBStreamHandler handler = new UCBStreamHandler(scheme, parent.m_xSFA); + + path += UCBStreamHandler.separator; + return new URL(null, path, handler); + } + + // TODO should decide whether this should throw or not + // decide whether it should be public or protected ( final ? ) + public void loadSource() { + try { + URL sourceUrl = getSourceURL(); + + LogUtils.DEBUG("** In load source BUT not loading yet for " + + sourceUrl); + + if (sourceUrl != null) { + StringBuilder buf = new StringBuilder(); + InputStream in = sourceUrl.openStream(); + + byte[] contents = new byte[1024]; + int len; + + while ((len = in.read(contents, 0, 1024)) != -1) { + buf.append(new String(contents, 0, len)); + } + + try { + in.close(); + } catch (java.io.IOException ignore) { + LogUtils.DEBUG("** Failed to read scriot from url " + + ignore.toString()); + } + + source = buf.toString(); + hasSource = true; + } + } catch (java.io.IOException e) { + LogUtils.DEBUG("** Failed to read scriot from url " + e.toString()); + } + + } + + protected boolean writeSourceFile() { + String sourceFilePath = parent.getPathToParcel() + "/" + getLanguageName(); + boolean result = false; + + try { + + XSimpleFileAccess2 xSFA2 = + UnoRuntime.queryInterface(XSimpleFileAccess2.class, parent.m_xSFA); + + if (xSFA2 != null) { + ByteArrayInputStream bis = new ByteArrayInputStream(getSourceBytes()); + XInputStreamImpl xis = new XInputStreamImpl(bis); + xSFA2.writeFile(sourceFilePath, xis); + xis.closeInput(); + result = true; + } + } + // TODO re-examine exception processing should probably throw + // exceptions back to caller + catch (Exception ignore) { + } + + return result; + } + + protected boolean removeSourceFile() { + String parcelLocation = parent.getPathToParcel(); + String sourceFilePath = parcelLocation + "/" + getLanguageName(); + boolean result = false; + + try { + parent.m_xSFA.kill(sourceFilePath); + result = true; + } + // TODO reexamine exception handling + catch (Exception e) { + } + + return result; + } + + public URL getSourceURL() throws java.net.MalformedURLException { + String sUrl = getParcelLocation(); + sUrl = PathUtils.make_url(sUrl, getLanguageName()); + LogUtils.DEBUG("Creating script url for " + sUrl); + return createURL(sUrl); + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/UnoPkgContainer.java b/scripting/java/com/sun/star/script/framework/container/UnoPkgContainer.java new file mode 100644 index 000000000..4b6a75520 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/UnoPkgContainer.java @@ -0,0 +1,401 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.container; + +import com.sun.star.deployment.XPackage; + +import com.sun.star.io.XOutputStream; +import com.sun.star.io.XTruncate; + +import com.sun.star.script.framework.io.XInputStreamWrapper; +import com.sun.star.script.framework.io.XOutputStreamWrapper; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import java.io.InputStream; +import java.io.OutputStream; + +import java.util.HashMap; +import java.util.Map; + +public class UnoPkgContainer extends ParcelContainer { + + private final Map<String, ParcelContainer> registeredPackages = new HashMap<String, ParcelContainer>(); + private final String extensionDb; + private final String extensionRepository; + + public UnoPkgContainer(XComponentContext xCtx, String locationURL, + String _extensionDb, String _extensionRepository, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + super(xCtx, locationURL, language, false); + extensionDb = _extensionDb; + extensionRepository = _extensionRepository; + init(); + } + + // gets the ParcelContainer for persisted uno packages + public ParcelContainer getRegisteredUnoPkgContainer(String url) { + + if (!url.endsWith("/")) { + url += "/"; + } + + LogUtils.DEBUG("** getRegisterPackage ctx = " + containerUrl); + LogUtils.DEBUG("** getRegisterPackage for uri " + url); + LogUtils.DEBUG("** getRegisterPackage for language " + language); + + ParcelContainer result = registeredPackages.get(url); + LogUtils.DEBUG("getRegisterPackage result is " + result); + return result; + } + + public boolean hasRegisteredUnoPkgContainer(String url) { + return getRegisteredUnoPkgContainer(url) != null; + } + + private void registerPackageContainer(String url, ParcelContainer c) { + + if (!url.endsWith("/")) { + url += "/"; + } + + LogUtils.DEBUG("RegisterPackage ctx = " + containerUrl); + LogUtils.DEBUG("RegisterPackage language = " + language); + LogUtils.DEBUG("RegisterPackage " + c + " for url " + url); + registeredPackages.put(url, c); + } + + public void deRegisterPackageContainer(String url) { + + if (!url.endsWith("/")) { + url += "/"; + } + + LogUtils.DEBUG("In deRegisterPackageContainer for " + url); + + if (hasRegisteredUnoPkgContainer(url)) { + try { + DeployedUnoPackagesDB db = getUnoPackagesDB(); + + if (db != null && db.removePackage(language, url)) { + writeUnoPackageDB(db); + ParcelContainer container = registeredPackages.get(url); + + if (!container.hasElements()) { + // When all libraries within a package bundle + // ( for this language ) are removed also + // remove the container from its parent + // Otherwise, a container ( with no containers ) + // representing the uno package bundle will + // still exist and so will get displayed + if (container.parent() != null) { + container.parent().removeChildContainer(container); + } + } + + registeredPackages.remove(url); + } + } catch (Exception e) { + //TODO revisit exception handling and exception here + //means something very wrong + LogUtils.DEBUG("***** deRegisterPackageContainer() got exception " + e); + } + } + + LogUtils.DEBUG("Leaving deRegisterPackageContainer for " + url); + } + + private void init() throws com.sun.star.lang.IllegalArgumentException { + LogUtils.DEBUG("getting container for " + containerUrl); + + try { + DeployedUnoPackagesDB db = getUnoPackagesDB(); + + if (db != null) { + String[] packages = db.getDeployedPackages(language); + + for (String thepackage : packages) { + try { + processUnoPackage(thepackage, language); + } catch (com.sun.star.lang.IllegalArgumentException ila) { + LogUtils.DEBUG("Failed to process " + thepackage + + " for " + language); + LogUtils.DEBUG(" Reason: " + ila); + } catch (Exception e) { + // TODO proper exception or do we wish + // to ignore errors here + LogUtils.DEBUG("Something very wrong!!!!!"); + LogUtils.DEBUG("Failed to process " + thepackage + + " for " + language); + LogUtils.DEBUG(" Reason: " + e); + } + } + } + } catch (com.sun.star.lang.WrappedTargetException e) { + // no deployed packages + LogUtils.DEBUG("No deployed uno-packages for " + containerUrl); + } + } + + @Override + public ScriptMetaData findScript(ParsedScriptUri psu) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + String functionName = psu.function; + String parcelName = psu.parcel; + String location = psu.location; + + LogUtils.DEBUG("*** UnoPkgContainer.findScript() ***" + + "\ncontainerUrl = " + containerUrl + + "\nfunction = " + functionName + + "\nlocation = " + location + + "\nparcel = " + parcelName); + + ParcelContainer pc = getChildContainer(location); + + if (pc == null) { + throw new com.sun.star.lang.WrappedTargetException( + "Failed to resolve script " , null, + new com.sun.star.lang.IllegalArgumentException( + "Cannot resolve script location for script = " + functionName)); + } + + return pc.findScript(psu); + } + + private DeployedUnoPackagesDB getUnoPackagesDB() throws + com.sun.star.lang.WrappedTargetException { + + InputStream is = null; + DeployedUnoPackagesDB dp = null; + + try { + + String packagesUrl = + PathUtils.make_url(extensionDb, + "/Scripts/" + extensionRepository + "-extension-desc.xml"); + + LogUtils.DEBUG("getUnoPackagesDB() looking for existing db in " + + packagesUrl); + + if (m_xSFA.exists(packagesUrl)) { + if (packagesUrl.startsWith("vnd.sun.star.tdoc")) { + // handles using XStorage directly + throw new com.sun.star.lang.WrappedTargetException( + "Can't handle documents yet"); + } + + is = new XInputStreamWrapper(m_xSFA.openFileRead(packagesUrl)); + dp = new DeployedUnoPackagesDB(is); + + try { + is.close(); + is = null; + } catch (Exception ignore) { + } + } else { + LogUtils.DEBUG("getUnoPackagesDB() " + packagesUrl + + " does not exist"); + } + } catch (Exception e) { + LogUtils.DEBUG("getUnoPackagesDB() caught Exception: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + throw new com.sun.star.lang.WrappedTargetException(e); + } finally { + if (is != null) { + try { + is.close(); + is = null; + } catch (Exception ignore) { + } + } + } + + return dp; + } + + private void writeUnoPackageDB(DeployedUnoPackagesDB dp) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("In writeUnoPackageDB() "); + + XOutputStream xos = null; + OutputStream os = null; + + try { + + String packagesUrl = + PathUtils.make_url(extensionDb, "/Scripts/" + extensionRepository + + "-extension-desc.xml"); + + xos = m_xSFA.openFileWrite(packagesUrl); + XTruncate xTrc = UnoRuntime.queryInterface(XTruncate.class, xos); + + if (xTrc != null) { + LogUtils.DEBUG("In writeUnoPackageDB() Truncating..."); + xTrc.truncate(); + } else { + LogUtils.DEBUG("In writeUnoPackageDB() CAN'T Truncate..."); + } + + os = new XOutputStreamWrapper(xos); + dp.write(os); + + try { + os.close(); // will close xos + os = null; + } catch (Exception ignore) { + } + } catch (Exception e) { + LogUtils.DEBUG("In writeUnoPackageDB() Exception: " + e); + throw new com.sun.star.lang.WrappedTargetException(e); + } finally { + if (os != null) { + try { + os.close(); // will close xos + os = null; + } catch (Exception ignore) { + } + } + } + } + + public void processUnoPackage(XPackage dPackage, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException, + com.sun.star.container.ElementExistException { + + LogUtils.DEBUG("** in processUnoPackage "); + + String uri = dPackage.getURL(); + + if (!uri.endsWith("/")) { + uri += "/"; + } + + LogUtils.DEBUG("** processUnoPackage getURL() -> " + uri); + LogUtils.DEBUG("** processUnoPackage getName() -> " + dPackage.getName()); + LogUtils.DEBUG("** processUnoPackage getMediaType() -> " + + dPackage.getPackageType().getMediaType()); + + try { + LogUtils.DEBUG("** processUnoPackage getDisplayName() -> " + + dPackage.getDisplayName()); + } catch (com.sun.star.deployment.ExtensionRemovedException e) { + throw new com.sun.star.lang.WrappedTargetException(e.getMessage(), this, e); + } + + processUnoPackage(uri, language); + + DeployedUnoPackagesDB db = getUnoPackagesDB(); + + if (db == null) { + try { + db = new DeployedUnoPackagesDB(); + } catch (java.io.IOException ioe) { + throw new com.sun.star.lang.WrappedTargetException(ioe); + } + } + + db.addPackage(language, uri); + writeUnoPackageDB(db); + } + + private void processUnoPackage(String uri, + String language) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException, + com.sun.star.container.ElementExistException { + + if (hasRegisteredUnoPkgContainer(uri)) { + throw new com.sun.star.container.ElementExistException( + "Already a registered uno package " + uri + " for language " + + language); + } + + LogUtils.DEBUG("processUnoPackage - URL = " + uri); + LogUtils.DEBUG("processUnoPackage - script library package"); + String parentUrl = uri; + + if (uri.contains("%2Funo_packages%2F") || + uri.contains("/uno_packages/") || + uri.contains("$UNO_USER_PACKAGES_CACHE/") || + uri.contains("$UNO_SHARED_PACKAGES_CACHE/") || + uri.contains("$BUNDLED_EXTENSIONS/")) { + + //its in a bundle need to determine the uno-package file its in + LogUtils.DEBUG("processUnoPackage - is part of a UNO bundle"); + + int index = uri.lastIndexOf('/'); + + if (uri.endsWith("/")) { + uri = uri.substring(0, index); + index = uri.lastIndexOf('/'); + } + + if (index > -1) { + parentUrl = uri.substring(0, index); + LogUtils.DEBUG("processUnoPackage - composition is contained in " + + parentUrl); + } + + ParcelContainer pkgContainer = getChildContainerForURL(parentUrl); + + if (pkgContainer == null) { + pkgContainer = + new ParcelContainer(this, m_xCtx, parentUrl, language, false); + + if (pkgContainer.loadParcel(uri) == null) { + throw new com.sun.star.lang.IllegalArgumentException( + "Couldn't load script library from composition package " + + uri + " for language " + language); + } + + addChildContainer(pkgContainer); + } else { + if (pkgContainer.loadParcel(uri) == null) { + throw new com.sun.star.lang.IllegalArgumentException( + "Couldn't load script library from composition package " + + uri + " for language " + language); + } + + } + + registerPackageContainer(uri, pkgContainer); + } else { + // stand-alone library package, e.g. not contained in + // a uno package + if (loadParcel(uri) == null) { + throw new com.sun.star.lang.IllegalArgumentException( + "Couldn't load script library package " + uri + + " for language " + language); + } + + registerPackageContainer(uri, this); + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/container/XMLParser.java b/scripting/java/com/sun/star/script/framework/container/XMLParser.java new file mode 100644 index 000000000..38e504ff4 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/XMLParser.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 . + */ + +package com.sun.star.script.framework.container; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.w3c.dom.Document; + +public interface XMLParser { + Document parse(InputStream inputStream) throws IOException; + void write(Document doc, OutputStream out) throws IOException; +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/container/XMLParserFactory.java b/scripting/java/com/sun/star/script/framework/container/XMLParserFactory.java new file mode 100644 index 000000000..02c9e6c34 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/container/XMLParserFactory.java @@ -0,0 +1,106 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.container; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; + +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class XMLParserFactory { + + private static XMLParser parser = null; + private static String officedtdurl = null; + + private XMLParserFactory() {} + + public static synchronized XMLParser getParser() { + if (parser == null) + parser = new DefaultParser(); + + return parser; + } + + public static void setOfficeDTDURL(String url) { + officedtdurl = url; + } + + private static class DefaultParser implements XMLParser { + + private final DocumentBuilderFactory factory; + + public DefaultParser() { + factory = DocumentBuilderFactory.newInstance(); + } + + public Document parse(InputStream inputStream) throws IOException { + + Document result = null; + + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + InputSource is = new InputSource(inputStream); + + if (officedtdurl != null) { + is.setSystemId(officedtdurl); + } + + result = builder.parse(is); + } catch (SAXParseException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (SAXException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (ParserConfigurationException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + + return result; + } + + public void write(Document doc, OutputStream out) throws IOException { + try { + TransformerFactory.newInstance().newTransformer().transform( + new DOMSource(doc), new StreamResult(out)); + } catch (TransformerException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/UCBStreamHandler.java b/scripting/java/com/sun/star/script/framework/io/UCBStreamHandler.java new file mode 100644 index 000000000..6413745d3 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/UCBStreamHandler.java @@ -0,0 +1,275 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.io; + +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.io.XTruncate; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.ucb.XSimpleFileAccess; + +import com.sun.star.uno.UnoRuntime; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + +import java.util.HashMap; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class UCBStreamHandler extends URLStreamHandler { + + public static final String separator = "/ucb/"; + + private final XSimpleFileAccess m_xSimpleFileAccess; + private final HashMap<String, InputStream> m_jarStreamMap = new HashMap<String, InputStream>(12); + private static String m_ucbscheme; + + public UCBStreamHandler(String scheme, XSimpleFileAccess xSFA) { + LogUtils.DEBUG("UCBStreamHandler ctor, scheme = " + scheme); + UCBStreamHandler.m_ucbscheme = scheme; + this.m_xSimpleFileAccess = xSFA; + } + + @Override + public void parseURL(URL url, String spec, int start, int limit) { + + LogUtils.DEBUG("**XUCBStreamHandler, parseURL: " + url + " spec: " + + spec + " start: " + start + " limit: " + limit); + + String file = url.getFile(); + + if (file == null) + file = spec.substring(start, limit); + else + file += spec.substring(start, limit); + + LogUtils.DEBUG("**For scheme = " + m_ucbscheme); + LogUtils.DEBUG("**Setting path = " + file); + setURL(url, m_ucbscheme, null, -1, null, null, file, null, null); + } + + @Override + public URLConnection openConnection(URL u) throws IOException { + return new UCBConnection(u); + } + + private class UCBConnection extends URLConnection { + + public UCBConnection(URL url) { + super(url); + } + + @Override + public void connect() { + } + + @Override + public InputStream getInputStream() throws IOException { + LogUtils.DEBUG("UCBConnectionHandler GetInputStream on " + url); + String sUrl = url.toString(); + + if (sUrl.lastIndexOf(separator) == -1) { + LogUtils.DEBUG("getInputStream straight file load"); + return getFileStreamFromUCB(sUrl); + } else { + String path = sUrl.substring(0, sUrl.lastIndexOf(separator)); + + String file = + sUrl.substring(sUrl.lastIndexOf(separator) + separator.length()); + + LogUtils.DEBUG("getInputStream, load of file from another file eg. " + + file + " from " + path); + + return getUCBStream(file, path); + } + } + @Override + public OutputStream getOutputStream() throws IOException { + LogUtils.DEBUG("UCBConnectionHandler getOutputStream on " + url); + OutputStream os = null; + + try { + String sUrl = url.toString(); + + if (sUrl.lastIndexOf(separator) != -1) { + String path = sUrl.substring(0, sUrl.lastIndexOf(separator)); + + if (m_xSimpleFileAccess.isReadOnly(path)) { + throw new java.io.IOException("File is read only"); + } + + LogUtils.DEBUG("getOutputStream, create o/p stream for file eg. " + + path); + + // we will only deal with simple file write + XOutputStream xos = m_xSimpleFileAccess.openFileWrite(path); + + XTruncate xtrunc = + UnoRuntime.queryInterface(XTruncate.class, xos); + + if (xtrunc != null) { + xtrunc.truncate(); + } + + os = new XOutputStreamWrapper(xos); + } + + if (os == null) { + throw new IOException("Failed to get OutputStream for " + + sUrl); + } + } catch (com.sun.star.ucb.CommandAbortedException cae) { + LogUtils.DEBUG("caught exception: " + cae.toString() + + " getting writable stream from " + url); + IOException newEx = new IOException(cae.getMessage()); + newEx.initCause(cae); + throw newEx; + } catch (com.sun.star.uno.Exception e) { + LogUtils.DEBUG("caught unknown exception: " + e.toString() + + " getting writable stream from " + url); + IOException newEx = new IOException(e.getMessage()); + newEx.initCause(e); + throw newEx; + } + + return os; + } + } + + private InputStream getUCBStream(String file, String path) throws IOException { + + InputStream is = null; + InputStream result = null; + + try { + if (path.endsWith(".jar")) { + is = m_jarStreamMap.get(path); + + if (is == null) { + is = getFileStreamFromUCB(path); + m_jarStreamMap.put(path, is); + } else { + try { + is.reset(); + } catch (IOException e) { + is.close(); + is = getFileStreamFromUCB(path); + m_jarStreamMap.put(path, is); + } + } + + result = getFileStreamFromJarStream(file, is); + } else { + String fileUrl = PathUtils.make_url(path, file); + result = getFileStreamFromUCB(fileUrl); + } + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ioe) { + LogUtils.DEBUG("Caught exception closing stream: " + + ioe.getMessage()); + } + } + } + + return result; + } + + private InputStream getFileStreamFromJarStream(String file, + InputStream is) throws + IOException { + + ZipInputStream zis = new ZipInputStream(is); + + while (zis.available() != 0) { + ZipEntry entry = zis.getNextEntry(); + + if (entry != null && entry.getName().equals(file)) { + return zis; + } + } + + return null; + } + + private InputStream getFileStreamFromUCB(String path) throws IOException { + + InputStream result = null; + XInputStream xInputStream = null; + + try { + LogUtils.DEBUG("Trying to read from " + path); + xInputStream = m_xSimpleFileAccess.openFileRead(path); + LogUtils.DEBUG("sfa appeared to read file "); + byte[][] inputBytes = new byte[1][]; + + int sz = m_xSimpleFileAccess.getSize(path); + + // TODO don't depend on result of available() or size() + // just read stream 'till complete + if (sz == 0 && xInputStream.available() > 0) { + sz = xInputStream.available(); + } + + LogUtils.DEBUG("size of file " + path + " is " + sz); + LogUtils.DEBUG("available = " + xInputStream.available()); + inputBytes[0] = new byte[sz]; + + int ln = xInputStream.readBytes(inputBytes, sz); + + if (ln != sz) { + throw new IOException( + "Failed to read " + sz + " bytes from XInputStream"); + } + + result = new ByteArrayInputStream(inputBytes[0]); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } catch (com.sun.star.uno.Exception ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } finally { + if (xInputStream != null) { + try { + xInputStream.closeInput(); + } catch (Exception e2) { + LogUtils.DEBUG("Error closing XInputStream: " + + e2.getMessage()); + } + } + } + + return result; + } +} diff --git a/scripting/java/com/sun/star/script/framework/io/XInputStreamImpl.java b/scripting/java/com/sun/star/script/framework/io/XInputStreamImpl.java new file mode 100644 index 000000000..8320b6227 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XInputStreamImpl.java @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.io; + +import com.sun.star.io.XInputStream; + +import java.io.IOException; +import java.io.InputStream; + +public class XInputStreamImpl implements XInputStream { + + private final InputStream is; + + public XInputStreamImpl(InputStream is) { + this.is = is; + } + + public int readBytes(/*OUT*/byte[][] aData, /*IN*/int nBytesToRead) throws + com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + + aData[ 0 ] = new byte[ nBytesToRead ]; + int totalBytesRead = 0; + + try { + int bytesRead; + + while ((bytesRead = is.read(aData[ 0 ], totalBytesRead, nBytesToRead)) > 0 + && (totalBytesRead < nBytesToRead)) { + totalBytesRead += bytesRead; + nBytesToRead -= bytesRead; + } + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } catch (IndexOutOfBoundsException aie) { + throw new com.sun.star.io.BufferSizeExceededException(aie); + } + + return totalBytesRead; + } + + public int readSomeBytes(/*OUT*/byte[][] aData, /*IN*/int nMaxBytesToRead) + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + + int bytesToRead = nMaxBytesToRead; + int availableBytes = available(); + + if (availableBytes < nMaxBytesToRead) { + bytesToRead = availableBytes; + } + + int read = readBytes(aData, bytesToRead); + return read; + } + + public void skipBytes(/*IN*/int nBytesToSkip) throws + com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + + try { + do { + nBytesToSkip -= is.skip(nBytesToSkip); + } while (nBytesToSkip > 0); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } + + public int available() throws + com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + + int bytesAvail = 0; + + try { + bytesAvail = is.available(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + + return bytesAvail; + } + + public void closeInput() throws + com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + + try { + is.close(); + } catch (IOException e) { + throw new com.sun.star.io.IOException(e); + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper.java b/scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper.java new file mode 100644 index 000000000..654f0e24c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XInputStreamWrapper.java @@ -0,0 +1,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/. + * + * 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 . + */ +package com.sun.star.script.framework.io; + +import com.sun.star.io.XInputStream; + +import java.io.IOException; +import java.io.InputStream; + +public class XInputStreamWrapper extends InputStream { + + private final XInputStream m_xInputStream; + + public XInputStreamWrapper(XInputStream xInputStream) { + m_xInputStream = xInputStream; + } + + @Override + public int read() throws java.io.IOException { + byte[][] byteRet = new byte[1][0]; + long numRead; + + try { + numRead = m_xInputStream.readBytes(byteRet, 1); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + + if (numRead != 1) { + return -1; + } + + return byteRet[0][0]; + } + + @Override + public int read(byte[] b) throws java.io.IOException { + byte[][] byteRet = new byte[1][]; + byteRet[0] = b; + + try { + return m_xInputStream.readBytes(byteRet, b.length); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public long skip(long n) throws java.io.IOException { + try { + m_xInputStream.skipBytes((int)n); + return n; + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public int available() throws java.io.IOException { + try { + return m_xInputStream.available(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void close() throws java.io.IOException { + try { + m_xInputStream.closeInput(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper.java b/scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper.java new file mode 100644 index 000000000..16357fd17 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XOutputStreamWrapper.java @@ -0,0 +1,113 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.io; + +import com.sun.star.io.XOutputStream; + +import java.io.IOException; +import java.io.OutputStream; + +public class XOutputStreamWrapper extends OutputStream { + + private final XOutputStream m_xOutputStream; + + public XOutputStreamWrapper(XOutputStream xOs) { + this.m_xOutputStream = xOs; + } + + @Override + public void write(int b) throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + byte[] bytes = new byte[1]; + bytes[0] = (byte) b; + + try { + m_xOutputStream.writeBytes(bytes); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void write(byte[] b) throws java.io.IOException { + + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + try { + m_xOutputStream.writeBytes(b); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + @Override + public void write(byte[] b, int off, int len) throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + byte[] bytes = new byte[len]; + System.arraycopy(b, off, bytes, 0, len); + + try { + m_xOutputStream.writeBytes(bytes); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void flush() throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + try { + m_xOutputStream.flush(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + @Override + public void close() throws java.io.IOException { + if (m_xOutputStream == null) { + throw new java.io.IOException("Stream is null"); + } + + try { + m_xOutputStream.closeOutput(); + } catch (com.sun.star.io.IOException ex1) { + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/io/XStorageHelper.java b/scripting/java/com/sun/star/script/framework/io/XStorageHelper.java new file mode 100644 index 000000000..79c5f10bc --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/io/XStorageHelper.java @@ -0,0 +1,249 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.io; + +import com.sun.star.beans.XPropertySet; + +import com.sun.star.container.XNameAccess; + +import com.sun.star.document.XDocumentSubStorageSupplier; + +import com.sun.star.embed.XStorage; +import com.sun.star.embed.XTransactedObject; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.EventObject; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.PathUtils; + +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; + +import java.io.IOException; + +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +public class XStorageHelper implements XEventListener { + + XStorage[] xStorages; + static Map<String, XModel> modelMap = new HashMap<String, XModel>(); + XModel xModel = null; + private static XStorageHelper listener = new XStorageHelper(); + + private XStorageHelper() {} + + public XStorageHelper(String path, int mode, + boolean create) throws IOException { + + String modelUrl = null; + int indexOfScriptsDir = path.lastIndexOf("Scripts"); + + if (indexOfScriptsDir > -1) { + modelUrl = path.substring(0, indexOfScriptsDir - 1); + path = path.substring(indexOfScriptsDir, path.length()); + } + + LogUtils.DEBUG("XStorageHelper ctor, path: " + path); + this.xModel = getModelForURL(modelUrl); + + try { + StringTokenizer tokens = new StringTokenizer(path, "/"); + + if (tokens.countTokens() == 0) { + throw new IOException("Invalid path"); + } + + XDocumentSubStorageSupplier xDocumentSubStorageSupplier = + UnoRuntime.queryInterface(XDocumentSubStorageSupplier.class, + xModel); + + xStorages = new XStorage[tokens.countTokens()]; + + LogUtils.DEBUG("XStorageHelper ctor, path chunks length: " + + xStorages.length); + + for (int i = 0; i < xStorages.length; i++) { + LogUtils.DEBUG("XStorageHelper, processing index " + i); + String name = tokens.nextToken(); + LogUtils.DEBUG("XStorageHelper, getting: " + name); + XStorage storage = null; + + if (i == 0) { + storage = xDocumentSubStorageSupplier.getDocumentSubStorage(name, mode); + + if (storage == null) { + LogUtils.DEBUG("** boo hoo Storage is null "); + } + + XPropertySet xProps = + UnoRuntime.queryInterface(XPropertySet.class, storage); + + if (xProps != null) { + + String mediaType = + AnyConverter.toString(xProps.getPropertyValue("MediaType")); + + LogUtils.DEBUG("***** media type is " + mediaType); + + if (!mediaType.equals("scripts")) { + xProps.setPropertyValue("MediaType", "scripts"); + } + } + } else { + + XNameAccess xNameAccess = + UnoRuntime.queryInterface(XNameAccess.class, xStorages[i - 1]); + + if (xNameAccess == null) { + disposeObject(); + throw new IOException("No name access " + name); + } else if (!xNameAccess.hasByName(name) + || !xStorages[i - 1].isStorageElement(name)) { + if (!create) { + disposeObject(); + throw new IOException("No subdir: " + name); + } else { + // attempt to create new storage + LogUtils.DEBUG("Attempt to create new storage for " + + name); + } + } + + storage = xStorages[i - 1].openStorageElement( + name, mode); + } + + if (storage == null) { + disposeObject(); + throw new IOException("storage not found: " + name); + } + + xStorages[i] = storage; + + } + } catch (com.sun.star.io.IOException ioe) { + disposeObject(); + } catch (com.sun.star.uno.Exception ex1) { + disposeObject(); + IOException ex2 = new IOException(); + ex2.initCause(ex1); + throw ex2; + } + } + + public synchronized static void addNewModel(XModel model) { + // TODO needs to cater for model for untitled document + modelMap.put(PathUtils.getOidForModel(model), model); + XComponent xComp = UnoRuntime.queryInterface(XComponent.class, model); + + if (xComp != null) { + try { + xComp.addEventListener(listener); + } catch (Exception e) { + // What TODO here ? + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + } + + public void disposing(EventObject Source) { + XModel model = UnoRuntime.queryInterface(XModel.class, Source.Source); + + if (model != null) { + LogUtils.DEBUG(" Disposing doc " + model.getURL()); + modelMap.remove(PathUtils.getOidForModel(model)); + } + } + + public XStorage getStorage() { + return xStorages[ xStorages.length - 1 ]; + } + + public XModel getModel() { + return xModel; + } + + public void disposeObject() { + disposeObject(false); + } + + public void disposeObject(boolean shouldCommit) { + LogUtils.DEBUG("In disposeObject"); + + for (int i = xStorages.length - 1 ; i > -1; i--) { + LogUtils.DEBUG("In disposeObject disposing storage " + i); + + try { + XStorage xStorage = xStorages[i]; + + if (shouldCommit) { + commit(xStorage); + } + + disposeObject(xStorage); + LogUtils.DEBUG("In disposeObject disposed storage " + i); + } catch (Exception ignore) { + LogUtils.DEBUG("Exception disposing storage " + i); + } + + } + + } + + public static void disposeObject(XInterface xInterface) { + if (xInterface == null) { + return; + } + + XComponent xComponent = + UnoRuntime.queryInterface(XComponent.class, xInterface); + + if (xComponent == null) { + return; + } + + xComponent.dispose(); + } + + public static void commit(XInterface xInterface) { + + XTransactedObject xTrans = + UnoRuntime.queryInterface(XTransactedObject.class, xInterface); + + if (xTrans != null) { + try { + xTrans.commit(); + } catch (Exception e) { + LogUtils.DEBUG("Something went belly up exception: " + e); + } + } + } + + public XModel getModelForURL(String url) { + //TODO does not cater for untitled documents + return modelMap.get(url); + } +} diff --git a/scripting/java/com/sun/star/script/framework/log/LogUtils.java b/scripting/java/com/sun/star/script/framework/log/LogUtils.java new file mode 100644 index 000000000..cd42b9d1e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/log/LogUtils.java @@ -0,0 +1,57 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.log; + +import java.io.StringWriter; +import java.io.PrintWriter; + +public class LogUtils { + + private static boolean m_bDebugEnabled = false; + + static { + String debugFlag = + System.getProperties().getProperty("ScriptJavaRuntimeDebug"); + + if (debugFlag != null && debugFlag.length() > 0) { + m_bDebugEnabled = debugFlag.equalsIgnoreCase("true"); + } + } + + // Ensure that instances of this class cannot be created + private LogUtils() { + } + + /** + * Print Debug Output + * + * @param msg message to be displayed + */ + public static void DEBUG(String msg) { + if (m_bDebugEnabled) { + System.out.println(msg); + } + } + + public static String getTrace(Exception e) { + StringWriter w = new StringWriter(); + e.printStackTrace(new PrintWriter(w)); + return w.toString(); + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory.java b/scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory.java new file mode 100644 index 000000000..b5e6e3085 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ClassLoaderFactory.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; + +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Class Loader Factory + */ +public class ClassLoaderFactory { + + private ClassLoaderFactory() {} + + public static ClassLoader getURLClassLoader(ScriptMetaData scriptData) { + ClassLoader parent = scriptData.getClass().getClassLoader(); + URL[] classPath = scriptData.getClassPath(); + LogUtils.DEBUG("Classpath has length " + classPath.length); + + for (int i = 0; i < classPath.length; i++) { + LogUtils.DEBUG("ClassPath " + i + "} is " + classPath[ i ].toString()); + } + + return getURLClassLoader(parent, classPath); + } + + public static ClassLoader getURLClassLoader(final ClassLoader parent, + final URL[] classpath) { + return AccessController.doPrivileged( + new PrivilegedAction<URLClassLoader>() { + public URLClassLoader run() { return new URLClassLoader(classpath, parent); } + }); + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/EditorScriptContext.java b/scripting/java/com/sun/star/script/framework/provider/EditorScriptContext.java new file mode 100644 index 000000000..d107edcc7 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/EditorScriptContext.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XModel; + +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class EditorScriptContext implements XScriptContext { + + private final XDesktop m_xDeskTop; + private final XComponentContext m_xComponentContext; + + public EditorScriptContext(XComponentContext xmComponentContext, + XDesktop xDesktop) { + this.m_xComponentContext = xmComponentContext; + this.m_xDeskTop = xDesktop; + } + + /** + Obtain the document reference on which the script can operate + + @return XModel interface + */ + public XModel getDocument() { + + XModel xModel = + UnoRuntime.queryInterface(XModel.class, m_xDeskTop.getCurrentComponent()); + + return xModel; + } + + public XScriptInvocationContext getInvocationContext() { + + XScriptInvocationContext xContext = + UnoRuntime.queryInterface(XScriptInvocationContext.class, getDocument()); + + return xContext; + } + + /** + Obtain the desktop reference on which the script can operate + + @return XDesktop interface + */ + public XDesktop getDesktop() { + return m_xDeskTop; + } + + /** + Obtain the component context which the script can use to create other uno components + + @return XComponentContext interface + */ + public XComponentContext getComponentContext() { + return m_xComponentContext; + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/PathUtils.java b/scripting/java/com/sun/star/script/framework/provider/PathUtils.java new file mode 100644 index 000000000..08b859f4e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/PathUtils.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider; + +import com.sun.star.frame.XModel; +import com.sun.star.uno.IQueryInterface; + +import java.lang.reflect.Method; + +import java.util.StringTokenizer; + +public class PathUtils { + + public static String BOOTSTRAP_NAME; + private static boolean m_windows = false; + + static { + // detect if windows platform + if (System.getProperty("os.name").startsWith("Windows")) + m_windows = true; + + BOOTSTRAP_NAME = m_windows ? "bootstrap.ini" : "bootstraprc"; + } + + public static String getOidForModel(XModel xModel) { + String oid = ""; + + if (xModel != null) { + try { + + Method getOid = + IQueryInterface.class.getMethod("getOid", (java.lang.Class[])null); + + if (getOid != null) { + oid = (String)getOid.invoke(xModel, new Object[0]); + } + + } catch (Exception ignore) { + } + } + + return oid; + } + + static public String make_url(String baseUrl, String url) { + StringBuilder buff = new StringBuilder(baseUrl.length() + url.length()); + buff.append(baseUrl); + StringTokenizer t = new StringTokenizer(url, "/"); + + while (t.hasMoreElements()) { + if (buff.charAt(buff.length() - 1) != '/') { + buff.append('/'); + } + + try { + buff.append(java.net.URLEncoder.encode((String)t.nextElement(), "UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + return buff.toString(); + } + + private PathUtils() { + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptContext.java b/scripting/java/com/sun/star/script/framework/provider/ScriptContext.java new file mode 100644 index 000000000..1f73ffbd4 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptContext.java @@ -0,0 +1,143 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider; + +import com.sun.star.beans.PropertyAttribute; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; + +import com.sun.star.lib.uno.helper.PropertySet; + +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +public class ScriptContext extends PropertySet implements XScriptContext { + + private static final String HM_DOC_REF = "DocumentReference"; + private static final String HM_DESKTOP = "Desktop"; + private static final String HM_COMPONENT_CONTEXT = "ComponentContext"; + + private static final String DOC_URI = "SCRIPTING_DOC_URI"; + + public final XModel m_xModel; + private final XScriptInvocationContext m_xInvocationContext; + + private final XDesktop m_xDeskTop; + + private final XComponentContext m_xComponentContext; + + // property, accessed via reflection + public String m_sDocURI = null; + + private ScriptContext(XComponentContext xmComponentContext, XDesktop xDesktop, + XModel xModel, XScriptInvocationContext xInvocContext) { + + this.m_xDeskTop = xDesktop; + this.m_xComponentContext = xmComponentContext; + this.m_xModel = xModel; + this.m_xInvocationContext = xInvocContext; + + if (m_xModel != null) { + + registerProperty(DOC_URI, new Type(String.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_sDocURI"); + + } + + registerProperty(HM_DOC_REF, new Type(XModel.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_xModel"); + + registerProperty(HM_DESKTOP, new Type(XDesktop.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_xDeskTop"); + + registerProperty(HM_COMPONENT_CONTEXT, new Type(XDesktop.class), + (short)(PropertyAttribute.MAYBEVOID | PropertyAttribute.TRANSIENT), + "m_xComponentContext"); + } + + public static XScriptContext createContext(XModel xModel, + XScriptInvocationContext xInvocContext, + XComponentContext xCtxt, XMultiComponentFactory xMCF) { + + XScriptContext sc = null; + + try { + + Object xInterface = + xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xCtxt); + + XDesktop xDesktop = UnoRuntime.queryInterface(XDesktop.class, xInterface); + + if (xModel != null) { + sc = new ScriptContext(xCtxt, xDesktop, xModel, xInvocContext); + } else { + sc = new EditorScriptContext(xCtxt, xDesktop); + } + + } catch (Exception e) { + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + + return sc; + } + + + /** + Obtain the document reference on which the script can operate + + @return XModel interface + */ + public XModel getDocument() { + return m_xModel; + } + + public XScriptInvocationContext getInvocationContext() { + return m_xInvocationContext; + } + + /** + Obtain the desktop reference on which the script can operate + + @return XDesktop interface + */ + public XDesktop getDesktop() { + return m_xDeskTop; + } + + /** + Obtain the component context which the script can use to create other uno components + + @return XComponentContext interface + */ + public XComponentContext getComponentContext() { + return m_xComponentContext; + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptEditor.java b/scripting/java/com/sun/star/script/framework/provider/ScriptEditor.java new file mode 100644 index 000000000..32ebd2fef --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptEditor.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 . + */ + +package com.sun.star.script.framework.provider; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.provider.XScriptContext; + +public interface ScriptEditor { + Object execute() throws Exception; + void indicateErrorLine(int lineNum); + void edit(XScriptContext context, ScriptMetaData entry); + String getTemplate(); + String getExtension(); +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptEditorBase.java b/scripting/java/com/sun/star/script/framework/provider/ScriptEditorBase.java new file mode 100644 index 000000000..1a8bf33a5 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptEditorBase.java @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider; + +import com.sun.star.beans.NamedValue; +import com.sun.star.configuration.theDefaultProvider; +import com.sun.star.container.XNameAccess; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.UnoRuntime; + +import javax.swing.JOptionPane; +import javax.swing.JDialog; + +public abstract class ScriptEditorBase implements ScriptEditor { + public XScriptContext context; + + public void setContext(XScriptContext context) { + this.context = context; + } + + public boolean isMacroExecutionEnabled() { + XNameAccess xNameAccess = null; + try { + String sAccess = "com.sun.star.configuration.ConfigurationAccess"; + XMultiServiceFactory xMSFCfg = theDefaultProvider.get(context.getComponentContext()); + Object oAccess = xMSFCfg.createInstanceWithArguments(sAccess, + new Object[] { new NamedValue("nodepath", "org.openoffice.Office.Common/Security/Scripting") }); + xNameAccess = UnoRuntime.queryInterface(XNameAccess.class, oAccess); + Object result = xNameAccess.getByName("DisableMacrosExecution"); + boolean bMacrosDisabled = AnyConverter.toBoolean(result); + if (bMacrosDisabled) + return false; + } catch (com.sun.star.uno.Exception e) { + return false; + } + return true; + } + + // Wraps long error messages + private static class NarrowOptionPane extends JOptionPane { + private static final long serialVersionUID = 1L; + public int getMaxCharactersPerLineCount() { + return 100; + } + } + + public void showErrorMessage(String message) { + JOptionPane optionPane = new NarrowOptionPane(); + optionPane.setMessage(message); + optionPane.setMessageType(JOptionPane.ERROR_MESSAGE); + JDialog dialog = optionPane.createDialog(null, "Error"); + dialog.setVisible(true); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/ScriptProvider.java b/scripting/java/com/sun/star/script/framework/provider/ScriptProvider.java new file mode 100644 index 000000000..e48bc48fb --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/ScriptProvider.java @@ -0,0 +1,673 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider; + +import com.sun.star.beans.Property; +import com.sun.star.beans.XIntrospectionAccess; +import com.sun.star.beans.XPropertyChangeListener; +import com.sun.star.beans.XPropertySet; +import com.sun.star.beans.XPropertySetInfo; +import com.sun.star.beans.XVetoableChangeListener; + +import com.sun.star.container.XNameContainer; + +import com.sun.star.deployment.XPackage; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; +import com.sun.star.frame.XTransientDocumentsDocumentContentFactory; + +import com.sun.star.lang.XInitialization; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XTypeProvider; + +import com.sun.star.script.XInvocation; +import com.sun.star.script.browse.BrowseNodeTypes; +import com.sun.star.script.browse.XBrowseNode; +import com.sun.star.script.framework.browse.DialogFactory; +import com.sun.star.script.framework.browse.ProviderBrowseNode; +import com.sun.star.script.framework.container.ParcelContainer; +import com.sun.star.script.framework.container.ParsedScriptUri; +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.container.UnoPkgContainer; +import com.sun.star.script.framework.container.XMLParserFactory; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; +import com.sun.star.script.provider.XScriptContext; +import com.sun.star.script.provider.XScriptProvider; + +import com.sun.star.sdbc.XRow; + +import com.sun.star.ucb.Command; +import com.sun.star.ucb.UniversalContentBroker; +import com.sun.star.ucb.XCommandProcessor; +import com.sun.star.ucb.XContent; +import com.sun.star.ucb.XContentIdentifier; +import com.sun.star.ucb.XUniversalContentBroker; + +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Exception; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import com.sun.star.util.XMacroExpander; + +public abstract class ScriptProvider implements + XScriptProvider, XBrowseNode, XPropertySet, XInvocation, XInitialization, + XTypeProvider, XServiceInfo, XNameContainer { + + private final String[] __serviceNames = { + "com.sun.star.script.provider.ScriptProviderFor", + "com.sun.star.script.provider.LanguageScriptProvider", + "com.sun.star.script.browse.BrowseNode", + "com.sun.star.script.provider.ScriptProvider" + }; + + protected String language; + + protected XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + protected XModel m_xModel; + protected XScriptInvocationContext m_xInvocContext; + private ParcelContainer m_container; + + // proxies to helper objects which implement interfaces + private XPropertySet m_xPropertySetProxy; + private XInvocation m_xInvocationProxy; + + // TODO should this be implemented in this class + private XBrowseNode m_xBrowseNodeProxy; + private XScriptContext m_xScriptContext; + + public ScriptProvider(XComponentContext ctx, String language) { + this.language = language; + __serviceNames[0] += language; + + LogUtils.DEBUG("ScriptProvider: constructor - start. " + language); + + m_xContext = ctx; + + // Initialize DialogFactory class in case dialogs are required + DialogFactory.createDialogFactory(m_xContext); + + try { + m_xMultiComponentFactory = m_xContext.getServiceManager(); + + if (m_xMultiComponentFactory == null) { + throw new Exception("Error could not obtain a " + + "multicomponent factory - rethrowing Exception."); + } + + Object serviceObj = + m_xContext.getValueByName( + "/singletons/com.sun.star.util.theMacroExpander"); + + XMacroExpander me = + (XMacroExpander) AnyConverter.toObject( + new Type(XMacroExpander.class), serviceObj); + + XMLParserFactory.setOfficeDTDURL( + me.expandMacros( + "$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/dtd/officedocument/1_0/")); + + } catch (Exception e) { + LogUtils.DEBUG(LogUtils.getTrace(e)); + throw new com.sun.star.uno.RuntimeException( + e, "Error constructing ScriptProvider: " + e); + } + + LogUtils.DEBUG("ScriptProvider: constructor - finished."); + } + + synchronized public XScriptContext getScriptingContext() { + if (m_xScriptContext == null) { + m_xScriptContext = ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory); + } + + return m_xScriptContext; + } + + public void initialize(Object[] aArguments) + throws com.sun.star.uno.Exception { + LogUtils.DEBUG("entering XInit for language " + language); + boolean isPkgProvider = false; + + if (aArguments.length == 1) { + String contextUrl = null; + + if (AnyConverter.getType(aArguments[0]).getTypeClass().equals( + TypeClass.INTERFACE)) { + // try whether it denotes a XScriptInvocationContext + m_xInvocContext = + UnoRuntime.queryInterface(XScriptInvocationContext.class, aArguments[0]); + + if (m_xInvocContext != null) { + // if so, obtain the document - by definition, this must be + // the ScriptContainer + m_xModel = + UnoRuntime.queryInterface(XModel.class, m_xInvocContext.getScriptContainer()); + } else { + // otherwise, check whether it's an XModel + m_xModel = + UnoRuntime.queryInterface(XModel.class, aArguments[0]); + } + + if (m_xModel == null) { + throw new com.sun.star.uno.Exception( + "ScriptProvider argument must be either a string, a valid XScriptInvocationContext, " + + "or an XModel", this); + } + + contextUrl = getDocUrlFromModel(m_xModel); + m_container = new ParcelContainer(m_xContext, contextUrl, language); + + } else if (AnyConverter.isString(aArguments[0])) { + + String originalContextURL = AnyConverter.toString(aArguments[0]); + LogUtils.DEBUG("creating Application, path: " + originalContextURL); + contextUrl = originalContextURL; + + // TODO no support for packages in documents yet + if (originalContextURL.startsWith("vnd.sun.star.tdoc")) { + m_container = new ParcelContainer(m_xContext, contextUrl, language); + m_xModel = getModelFromDocUrl(originalContextURL); + } else { + String extensionDb = "vnd.sun.star.expand:${$BRAND_INI_DIR/" + + PathUtils.BOOTSTRAP_NAME + "::UserInstallation}/user"; + + String extensionRepository = null; + + if (originalContextURL.startsWith("bundled")) { + contextUrl = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS"; + extensionRepository = "bundled"; + } else if (originalContextURL.startsWith("share")) { + contextUrl = "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR"; + extensionRepository = "shared"; + } else if (originalContextURL.startsWith("user")) { + contextUrl = "vnd.sun.star.expand:${$BRAND_INI_DIR/" + + PathUtils.BOOTSTRAP_NAME + "::UserInstallation}/user"; + extensionRepository = "user"; + } + + if (originalContextURL.endsWith("uno_packages")) { + isPkgProvider = true; + + if (!originalContextURL.equals(contextUrl) + && extensionRepository != null + && !extensionRepository.equals("bundled")) { + + contextUrl = PathUtils.make_url(contextUrl, "uno_packages"); + } + } + + if (isPkgProvider) { + m_container = + new UnoPkgContainer(m_xContext, contextUrl, extensionDb, extensionRepository, + language); + } else { + m_container = + new ParcelContainer(m_xContext, contextUrl, language); + } + } + } else { + throw new com.sun.star.uno.RuntimeException( + "ScriptProvider created with invalid argument"); + } + + LogUtils.DEBUG("Modified Application path is: " + contextUrl); + LogUtils.DEBUG("isPkgProvider is: " + isPkgProvider); + + // TODO should all be done in this class instead of + // delegation???? + m_xBrowseNodeProxy = + new ProviderBrowseNode(this, m_container, m_xContext); + + m_xInvocationProxy = + UnoRuntime.queryInterface(XInvocation.class, m_xBrowseNodeProxy); + m_xPropertySetProxy = + UnoRuntime.queryInterface(XPropertySet.class, m_xBrowseNodeProxy); + } else { + // this is ok, for example when executing a script from the + // command line + LogUtils.DEBUG("ScriptProviderFor" + language + + " initialized without a context"); + } + + LogUtils.DEBUG("leaving XInit"); + } + + /** + * Gets the types attribute of the ScriptProvider object + * + * @return The types value + */ + public com.sun.star.uno.Type[] getTypes() { + Type[] retValue = new Type[ 8 ]; + retValue[ 0 ] = new Type(XScriptProvider.class); + retValue[ 1 ] = new Type(XBrowseNode.class); + retValue[ 2 ] = new Type(XInitialization.class); + retValue[ 3 ] = new Type(XTypeProvider.class); + retValue[ 4 ] = new Type(XServiceInfo.class); + retValue[ 5 ] = new Type(XPropertySet.class); + retValue[ 6 ] = new Type(XInvocation.class); + retValue[ 7 ] = new Type(com.sun.star.container.XNameContainer.class); + + return retValue; + } + + /** + * Gets the implementationId attribute of the ScriptProvider object + * + * @return The implementationId value + */ + public byte[] getImplementationId() { + return new byte[0]; + } + + /** + * Gets the implementationName attribute of the ScriptProvider object + * + * @return The implementationName value + */ + public String getImplementationName() { + return getClass().getName(); + } + + /** + * Description of the Method + * + * @param serviceName Description of the Parameter + * @return Description of the Return Value + */ + public boolean supportsService(String serviceName) { + for (int index = __serviceNames.length; index-- > 0;) { + if (serviceName.equals(__serviceNames[ index ])) { + return true; + } + } + + return false; + } + + /** + * Gets the supportedServiceNames attribute of the ScriptProvider object + * + * @return The supportedServiceNames value + */ + public String[] getSupportedServiceNames() { + return __serviceNames; + } + + public abstract XScript getScript(/*IN*/String scriptURI) throws + com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException; + + // TODO need to encapsulate this better, + // Some factory concept for creating/accessing Editor + // How this is passed down or how it is accessible by BrowseNode + // implementations needs thinking about + // This method is used to determine whether the ScriptProvider + // has a ScriptEditor + public abstract boolean hasScriptEditor(); + + // TODO see above + // This method is used to get the ScriptEditor for this ScriptProvider + public abstract ScriptEditor getScriptEditor(); + + public ScriptMetaData getScriptData(/*IN*/String scriptURI) throws + ScriptFrameworkErrorException { + + try { + ParsedScriptUri details = m_container.parseScriptUri(scriptURI); + + try { + ScriptMetaData scriptData = m_container.findScript(details); + + if (scriptData == null) { + throw new ScriptFrameworkErrorException( + details.function + " does not exist", null, details.function, language, + ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + } + + return scriptData; + } catch (com.sun.star.container.NoSuchElementException nse) { + ScriptFrameworkErrorException e2 = + new ScriptFrameworkErrorException(nse.getMessage(), null, details.function, + language, ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(nse); + throw e2; + } catch (com.sun.star.lang.WrappedTargetException wta) { + // TODO specify the correct error Type + java.lang.Exception wrapped = (java.lang.Exception) wta.TargetException; + String message = wta.getMessage(); + + if (wrapped != null) { + message = wrapped.getMessage(); + } + + ScriptFrameworkErrorException e2 = + new ScriptFrameworkErrorException(message, null, details.function, language, + ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(wta); + throw e2; + } + } catch (com.sun.star.lang.IllegalArgumentException ila) { + // TODO specify the correct error Type + ScriptFrameworkErrorException e2 + = new ScriptFrameworkErrorException( + ila.getMessage(), null, scriptURI, language, + ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(ila); + throw e2; + } + } + + // Implementation of XBrowseNode interface + public String getName() { + return language; + } + + public XBrowseNode[] getChildNodes() { + if (m_xBrowseNodeProxy == null) { + LogUtils.DEBUG("No Nodes available "); + return new XBrowseNode[0]; + } + + return m_xBrowseNodeProxy .getChildNodes(); + } + + public boolean hasChildNodes() { + if (m_xBrowseNodeProxy == null) { + LogUtils.DEBUG("No Nodes available "); + return false; + } + + return m_xBrowseNodeProxy.hasChildNodes(); + } + + public short getType() { + return BrowseNodeTypes.CONTAINER; + } + + @Override + public String toString() { + return getName(); + } + + // implementation of XInvocation interface + public XIntrospectionAccess getIntrospection() { + return m_xInvocationProxy.getIntrospection(); + } + + public Object invoke(String aFunctionName, Object[] aParams, + short[][] aOutParamIndex, Object[][] aOutParam) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + return m_xInvocationProxy.invoke( + aFunctionName, aParams, aOutParamIndex, aOutParam); + } + + public void setValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.script.CannotConvertException, + com.sun.star.reflection.InvocationTargetException { + + m_xInvocationProxy.setValue(aPropertyName, aValue); + } + + public Object getValue(String aPropertyName) throws + com.sun.star.beans.UnknownPropertyException { + return m_xInvocationProxy.getValue(aPropertyName); + } + + public boolean hasMethod(String aName) { + return m_xInvocationProxy.hasMethod(aName); + } + + public boolean hasProperty(String aName) { + return m_xInvocationProxy.hasProperty(aName); + } + + public XPropertySetInfo getPropertySetInfo() { + return m_xPropertySetProxy.getPropertySetInfo(); + } + + public void setPropertyValue(String aPropertyName, Object aValue) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.beans.PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.setPropertyValue(aPropertyName, aValue); + } + + public Object getPropertyValue(String PropertyName) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + return m_xPropertySetProxy.getPropertyValue(PropertyName); + } + + public void addPropertyChangeListener(String aPropertyName, + XPropertyChangeListener xListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.addPropertyChangeListener(aPropertyName, xListener); + } + + public void removePropertyChangeListener( + String aPropertyName, XPropertyChangeListener aListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.removePropertyChangeListener( + aPropertyName, aListener); + } + + public void addVetoableChangeListener( + String PropertyName, XVetoableChangeListener aListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.addVetoableChangeListener(PropertyName, aListener); + } + + public void removeVetoableChangeListener(String PropertyName, + XVetoableChangeListener aListener) throws + com.sun.star.beans.UnknownPropertyException, + com.sun.star.lang.WrappedTargetException { + + m_xPropertySetProxy.removeVetoableChangeListener( + PropertyName, aListener); + + } + public java.lang.Object getByName(String aName) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + // TODO needs implementing? + throw new com.sun.star.uno.RuntimeException("getByName not implemented"); + } + + public String[] getElementNames() { + // TODO needs implementing? + throw new com.sun.star.uno.RuntimeException("getElementNames not implemented"); + } + + // Performs the getRegStatus functionality for the PkgMgr + public boolean hasByName(String aName) { + return ((UnoPkgContainer)m_container).hasRegisteredUnoPkgContainer(aName); + } + + public com.sun.star.uno.Type getElementType() { + // TODO at the moment this returns void indicating + // type is unknown should indicate XPackage ? do we implement XPackage + return new Type(); + } + + public boolean hasElements() { + // TODO needs implementing? + throw new com.sun.star.uno.RuntimeException("hasElements not implemented"); + } + public void replaceByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + // TODO needs implementing + throw new com.sun.star.uno.RuntimeException("replaceByName not implemented"); + } + + public void insertByName(String aName, java.lang.Object aElement) throws + com.sun.star.lang.IllegalArgumentException, + com.sun.star.container.ElementExistException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("Provider for " + language + " received register for package " + + aName); + + XPackage newPackage = UnoRuntime.queryInterface(XPackage.class, aElement); + + if (aName == null || aName.length() == 0) { + throw new com.sun.star.lang.IllegalArgumentException("Empty name"); + } + + if (newPackage == null) { + throw new com.sun.star.lang.IllegalArgumentException("No package supplied"); + } + + ((UnoPkgContainer)m_container).processUnoPackage(newPackage, language); + } + + // de-register for library only !! + public void removeByName(String Name) throws + com.sun.star.container.NoSuchElementException, + com.sun.star.lang.WrappedTargetException { + + LogUtils.DEBUG("In ScriptProvider.removeByName() for " + Name + + " this provider = " + language); + + ParcelContainer c = + ((UnoPkgContainer)m_container).getRegisteredUnoPkgContainer( + Name); + + if (c != null) { + String libName; + + if (Name.endsWith("/")) { + String tmp = Name.substring(0, Name.lastIndexOf('/')); + libName = tmp.substring(tmp.lastIndexOf('/') + 1); + } else { + libName = Name.substring(Name.lastIndexOf('/') + 1); + } + + LogUtils.DEBUG("Deregistering library " + libName); + + if (c.removeParcel(libName)) { + ((UnoPkgContainer)m_container).deRegisterPackageContainer(Name); + } else { + throw new com.sun.star.container.NoSuchElementException( + libName + " cannot be removed from container."); + } + } else { + throw new com.sun.star.container.NoSuchElementException( + Name + " doesn't exist for " + language); + } + + // TODO see if we want to remove the ParcelContainer is no Parcels/Libraries left + } + + private String getDocUrlFromModel(XModel document) { + XTransientDocumentsDocumentContentFactory factory = null; + + try { + + factory = UnoRuntime.queryInterface( + XTransientDocumentsDocumentContentFactory.class, + m_xMultiComponentFactory.createInstanceWithContext( + "com.sun.star.frame.TransientDocumentsDocumentContentFactory", + m_xContext)); + + } catch (Exception ex) { + } + + if (factory == null) + throw new com.sun.star.uno.RuntimeException( + "ScriptProvider: unable to create a TDOC context factory.", this); + + try { + XContent content = factory.createDocumentContent(document); + return content.getIdentifier().getContentIdentifier(); + } catch (com.sun.star.lang.IllegalArgumentException ex) { + } + + LogUtils.DEBUG("unable to determine the model's TDOC URL"); + return ""; + } + + private XModel getModelFromDocUrl(String docUrl) { + + LogUtils.DEBUG("getModelFromDocUrl - searching for match for ->" + + docUrl + "<-"); + + XModel xModel = null; + + try { + XUniversalContentBroker ucb = UniversalContentBroker.create(m_xContext); + + XContentIdentifier xCntId = ucb.createContentIdentifier(docUrl); + + XContent xCnt = ucb.queryContent(xCntId); + + XCommandProcessor xCmd = + UnoRuntime.queryInterface(XCommandProcessor.class, xCnt); + + Property[] pArgs = new Property[ ] { new Property() }; + pArgs[ 0 ].Name = "DocumentModel"; + pArgs[ 0 ].Handle = -1; + + Command command = new Command(); + + command.Handle = -1; + command.Name = "getPropertyValues"; + command.Argument = pArgs; + + com.sun.star.ucb.XCommandEnvironment env = null ; + Object result = xCmd.execute(command, 0, env) ; + + XRow values = UnoRuntime.queryInterface(XRow.class, result); + + xModel = UnoRuntime.queryInterface(XModel.class, + values.getObject(1, null)); + + } catch (Exception ignore) { + LogUtils.DEBUG("Failed to get model exception " + ignore); + } + + return xModel; + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/SwingInvocation.java b/scripting/java/com/sun/star/script/framework/provider/SwingInvocation.java new file mode 100644 index 000000000..af563fa8e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/SwingInvocation.java @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider; + +import javax.swing.SwingUtilities; + +// On macOS, AWT/Swing must not be accessed from the AppKit thread, so call +// SwingUtilities.invokeLater always on a fresh thread to avoid that problem +// (also, the current thread must not wait for that fresh thread to terminate, +// as that would cause a deadlock if this thread is the AppKit thread): +public final class SwingInvocation { + + public static void invoke(final Runnable doRun) { + new Thread("SwingInvocation") { + @Override + public void run() { + SwingUtilities.invokeLater(doRun); + } + } .start(); + } + + private SwingInvocation() {} +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF b/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF new file mode 100644 index 000000000..689b6fde9 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.provider.beanshell.ScriptProviderForBeanShell +UNO-Type-Path: diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView.java new file mode 100644 index 000000000..e519587ff --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/PlainSourceView.java @@ -0,0 +1,395 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.beanshell; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.UIManager; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.text.BadLocationException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; +import java.util.List; +import java.util.ArrayList; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PlainSourceView extends JScrollPane implements + ScriptSourceView, DocumentListener { + + private final ScriptSourceModel model; + private JTextArea ta; + private GlyphGutter gg; + private int linecount; + private boolean isModified = false; + private static final String undoKey = "Undo"; + private static final String redoKey = "Redo"; + private CompoundEdit compoundEdit = null; + private static final int noLimit = -1; + UndoManager undoManager; + private List<UnsavedChangesListener> unsavedListener = new ArrayList<UnsavedChangesListener>(); + + private static final Pattern tabPattern = Pattern.compile("^ *(\\t)"); + private static final Pattern indentationPattern = Pattern.compile("^([^\\S\\r\\n]*)(([^\\{])*\\{\\s*)*"); + + public PlainSourceView(ScriptSourceModel model) { + this.model = model; + initUI(); + model.setView(this); + } + + public void undo(){ + if(compoundEdit!=null){ + compoundEdit.end(); + undoManager.addEdit(compoundEdit); + compoundEdit = null; + } + if(undoManager.canUndo()){ + undoManager.undo(); + } + // check if it's the last undoable change + if(undoManager.canUndo() == false){ + setModified(false); + } + } + public void redo(){ + if(undoManager.canRedo()){ + undoManager.redo(); + } + } + public void clear() { + ta.setText(""); + } + + public void update() { + /* Remove ourselves as a DocumentListener while loading the source + so we don't get a storm of DocumentEvents during loading */ + ta.getDocument().removeDocumentListener(this); + + if (!isModified) { + int pos = ta.getCaretPosition(); + ta.setText(model.getText()); + + try { + ta.setCaretPosition(pos); + } catch (IllegalArgumentException iae) { + // do nothing and allow JTextArea to set its own position + } + } + + // scroll to currentPosition of the model + try { + int line = ta.getLineStartOffset(model.getCurrentPosition()); + Rectangle rect = ta.modelToView(line); + if (rect != null) { + ta.scrollRectToVisible(rect); + } + } catch (BadLocationException e) { + // couldn't scroll to line, do nothing + } + + gg.repaint(); + + // Add back the listener + ta.getDocument().addDocumentListener(this); + } + + public boolean isModified() { + return isModified; + } + + private void notifyListeners (boolean isUnsaved) { + for (UnsavedChangesListener listener : unsavedListener) { + listener.onUnsavedChanges(isUnsaved); + } + } + + public void setModified(boolean value) { + if(value != isModified) { + notifyListeners(value); + isModified = value; + } + } + + private void initUI() { + try{ + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch(Exception e){ + // What to do here + } + ta = new JTextArea(); + ta.setTabSize(4); + ta.setRows(15); + ta.setColumns(40); + ta.setLineWrap(false); + ta.insert(model.getText(), 0); + ta.setFont(new Font("Monospaced", ta.getFont().getStyle(), ta.getFont().getSize())); + undoManager = new UndoManager(); + undoManager.setLimit(noLimit); + ta.getDocument().addUndoableEditListener(new UndoableEditListener(){ + @Override + public void undoableEditHappened(UndoableEditEvent editEvent) { + if(compoundEdit == null){ + compoundEdit = new CompoundEdit(); + } + compoundEdit.addEdit(editEvent.getEdit()); + } + }); + + ta.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK), undoKey); + ta.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK), redoKey); + + ta.addKeyListener(new KeyAdapter(){ + @Override + public void keyPressed(KeyEvent ke) { + // if shift + tab was pressed, remove the first tab before any code begins + if (ke.isShiftDown() && ke.getKeyCode() == KeyEvent.VK_TAB) { + try { + int caretOffset = ta.getCaretPosition(); + int lineOffset = ta.getLineOfOffset(caretOffset); + int startOffset = ta.getLineStartOffset(lineOffset); + int endOffset = ta.getLineEndOffset(lineOffset); + + Matcher matcher = tabPattern.matcher(ta.getText(startOffset, endOffset - startOffset)); + if (matcher.find()) { + ta.replaceRange(null, startOffset + matcher.start(1), startOffset + matcher.end(1)); + } + } catch (BadLocationException e) { + // could not find correct location of the tab + } + } + // if the enter key was pressed, adjust indentation of the current line accordingly + if (ke.getKeyCode() == KeyEvent.VK_ENTER) { + try { + int caretOffset = ta.getCaretPosition(); + int lineOffset = ta.getLineOfOffset(caretOffset); + int startOffset = ta.getLineStartOffset(lineOffset); + int endOffset = ta.getLineEndOffset(lineOffset); + + Matcher matcher = indentationPattern.matcher(ta.getText(startOffset, endOffset - startOffset)); + // insert new line including indentation of the previous line + ta.insert("\n", caretOffset++); + if (matcher.find()) { + if (matcher.group(1).length() > 0) { + ta.insert(matcher.group(1), caretOffset++); + } + // if there is an open curly bracket in the current line, increase indentation level + if (matcher.group(3) != null) { + ta.insert("\t", caretOffset); + } + } + ke.consume(); + } catch (BadLocationException e) { + // could not find correct location of the indentation + } + } + } + + @Override + public void keyReleased(KeyEvent ke){ + if(ke.getKeyCode() == KeyEvent.VK_SPACE || ke.getKeyCode() == KeyEvent.VK_ENTER){ + compoundEdit.end(); + undoManager.addEdit(compoundEdit); + compoundEdit = null; + } + } + }); + + ta.getActionMap().put(undoKey, new AbstractAction(undoKey){ + @Override + public void actionPerformed(ActionEvent event) { + undo(); + } + }); + + ta.getActionMap().put(redoKey, new AbstractAction(redoKey){ + @Override + public void actionPerformed(ActionEvent event) { + redo(); + } + }); + + linecount = ta.getLineCount(); + + gg = new GlyphGutter(this); + + setViewportView(ta); + setRowHeaderView(gg); + + ta.getDocument().addDocumentListener(this); + } + + /* Implementation of DocumentListener interface */ + public void insertUpdate(DocumentEvent e) { + doChanged(); + } + + public void removeUpdate(DocumentEvent e) { + doChanged(); + } + + public void changedUpdate(DocumentEvent e) { + doChanged(); + } + + /* If the number of lines in the JTextArea has changed then update the + GlyphGutter */ + private void doChanged() { + setModified(true); + + if (linecount != ta.getLineCount()) { + gg.update(); + linecount = ta.getLineCount(); + } + } + + public String getText() { + return ta.getText(); + } + + public JTextArea getTextArea() { + return ta; + } + + public int getCurrentPosition() { + return model.getCurrentPosition(); + } + + public void addListener(UnsavedChangesListener toAdd) { + unsavedListener.add(toAdd); + } +} + +class GlyphGutter extends JComponent { + + private final PlainSourceView view; + private static final String DUMMY_STRING = "99"; + + GlyphGutter(PlainSourceView view) { + this.view = view; + update(); + } + + public void update() { + JTextArea textArea = view.getTextArea(); + Font font = textArea.getFont(); + setFont(font); + + FontMetrics metrics = getFontMetrics(font); + int h = metrics.getHeight(); + int lineCount = textArea.getLineCount() + 1; + + String dummy = Integer.toString(lineCount); + + if (dummy.length() < 2) { + dummy = DUMMY_STRING; + } + + Dimension d = new Dimension(); + d.width = metrics.stringWidth(dummy) + 16; + d.height = lineCount * h + 100; + setPreferredSize(d); + setSize(d); + } + + @Override + public void paintComponent(Graphics g) { + JTextArea textArea = view.getTextArea(); + + Font font = textArea.getFont(); + g.setFont(font); + + FontMetrics metrics = getFontMetrics(font); + Rectangle clip = g.getClipBounds(); + + g.setColor(getBackground()); + g.fillRect(clip.x, clip.y, clip.width, clip.height); + + int ascent = metrics.getMaxAscent(); + int h = metrics.getHeight(); + int lineCount = textArea.getLineCount() + 1; + + int startLine = clip.y / h; + int endLine = (clip.y + clip.height) / h + 1; + int width = getWidth(); + + if (endLine > lineCount) { + endLine = lineCount; + } + + for (int i = startLine; i < endLine; i++) { + String text; + text = Integer.toString(i + 1) + " "; + int y = i * h; + g.setColor(Color.blue); + g.drawString(text, 0, y + ascent); + int x = width - ascent; + + // if currentPosition is not -1 then a red arrow will be drawn + if (i == view.getCurrentPosition()) { + drawArrow(g, ascent, x, y); + } + } + } + + private void drawArrow(Graphics g, int ascent, int x, int y) { + Polygon arrow = new Polygon(); + int dx = x; + y += ascent - 10; + int dy = y; + arrow.addPoint(dx, dy + 3); + arrow.addPoint(dx + 5, dy + 3); + + for (x = dx + 5; x <= dx + 10; x++, y++) { + arrow.addPoint(x, y); + } + + for (x = dx + 9; x >= dx + 5; x--, y++) { + arrow.addPoint(x, y); + } + + arrow.addPoint(dx + 5, dy + 7); + arrow.addPoint(dx, dy + 7); + + g.setColor(Color.red); + g.fillPolygon(arrow); + g.setColor(Color.black); + g.drawPolygon(arrow); + } +} + diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell.java new file mode 100644 index 000000000..0ab265ecc --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptEditorForBeanShell.java @@ -0,0 +1,400 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.beanshell; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptEditorBase; +import com.sun.star.script.framework.provider.SwingInvocation; +import com.sun.star.script.provider.XScriptContext; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.Dimension; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.URL; + +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import javax.swing.JToolBar; +import javax.swing.BorderFactory; + +public class ScriptEditorForBeanShell extends ScriptEditorBase implements ActionListener { + + private JFrame frame; + private String filename; + + private ScriptSourceModel model; + private ScriptSourceView view; + + private URL scriptURL = null; + private ClassLoader cl = null; + private JButton saveBtn; + + // global ScriptEditorForBeanShell returned for getEditor() calls + private static ScriptEditorForBeanShell theScriptEditorForBeanShell; + + // global list of ScriptEditors, key is [external form of URL] of file being edited + private static Map<String, ScriptEditorForBeanShell> BEING_EDITED = + new HashMap<String, ScriptEditorForBeanShell>(); + + // template for new BeanShell scripts + private static String BSHTEMPLATE; + + // try to load the template for BeanShell scripts + static { + BSHTEMPLATE = "// BeanShell script"; + try { + URL url = ScriptEditorForBeanShell.class.getResource("template.bsh"); + if (url != null) { + InputStream in = url.openStream(); + StringBuilder buf = new StringBuilder(); + byte[] b = new byte[1024]; + int len; + + while ((len = in.read(b)) != -1) { + buf.append(new String(b, 0, len)); + } + + in.close(); + + BSHTEMPLATE = buf.toString(); + } + } catch (IOException ioe) { + } catch (Exception e) { + } + } + + /** + * Returns the global ScriptEditorForBeanShell instance. + */ + + public static synchronized ScriptEditorForBeanShell getEditor() { + if (theScriptEditorForBeanShell == null) { + theScriptEditorForBeanShell = + new ScriptEditorForBeanShell(); + } + + return theScriptEditorForBeanShell; + } + + /** + * Get the ScriptEditorForBeanShell instance for this URL + * + * @param url The URL of the script source file + * + * @return The ScriptEditorForBeanShell associated with + * the given URL if one exists, otherwise null. + */ + public static ScriptEditorForBeanShell getEditor(URL url) { + synchronized (BEING_EDITED) { + return BEING_EDITED.get(url.toExternalForm()); + } + } + + /** + * Returns whether or not the script source being edited in this + * ScriptEditorForBeanShell has been modified + */ + public boolean isModified() { + return view.isModified(); + } + + /** + * Returns the text being displayed in this ScriptEditorForBeanShell + * + * @return The text displayed in this ScriptEditorForBeanShell + */ + public String getText() { + return view.getText(); + } + + /** + * Returns the template text for BeanShell scripts + * + * @return The template text for BeanShell scripts + */ + public String getTemplate() { + return BSHTEMPLATE; + } + + /** + * Returns the default extension for BeanShell scripts + * + * @return The default extension for BeanShell scripts + */ + public String getExtension() { + return "bsh"; + } + + /** + * Indicates the line where error occurred + * + */ + public void indicateErrorLine(int lineNum) { + model.indicateErrorLine(lineNum); + } + + /** + * Executes the script edited by the editor + * + */ + public Object execute() throws Exception { + if (!isMacroExecutionEnabled()) { + showErrorMessage("Macro Execution has been disabled."); + return null; + } + + frame.toFront(); + return model.execute(context, cl); + } + + /** + * Opens an editor window for the specified ScriptMetaData. + * If an editor window is already open for that data it will be + * moved to the front. + * + * @param context The context in which to execute the script + * @param entry The metadata describing the script + */ + public void edit(final XScriptContext context, ScriptMetaData entry) { + if (entry != null) { + try { + ClassLoader cl = null; + + try { + cl = ClassLoaderFactory.getURLClassLoader(entry); + } catch (Exception ignore) { // TODO re-examine error handling + } + + final ClassLoader theCl = cl; + final URL url = entry.getSourceURL(); + SwingInvocation.invoke( + new Runnable() { + public void run() { + ScriptEditorForBeanShell editor; + + synchronized (BEING_EDITED) { + editor = BEING_EDITED.get(url.toExternalForm()); + + if (editor == null) { + editor = new ScriptEditorForBeanShell( + context, theCl, url); + BEING_EDITED.put(url.toExternalForm(), editor); + } + } + + editor.frame.toFront(); + } + }); + } catch (IOException ioe) { + showErrorMessage("Error loading file: " + ioe.getMessage()); + } + } + } + + private ScriptEditorForBeanShell() { + } + + private ScriptEditorForBeanShell(XScriptContext context, ClassLoader cl, + URL url) { + setContext(context); + this.scriptURL = url; + this.model = new ScriptSourceModel(url); + this.filename = ScriptMetaData.getFileName(url); + this.cl = cl; + + try { + + Class<?> c = + Class.forName("org.openoffice.netbeans.editor.NetBeansSourceView"); + + Class<?>[] types = new Class[] { ScriptSourceModel.class }; + + java.lang.reflect.Constructor<?> ctor = c.getConstructor(types); + + if (ctor != null) { + Object[] args = new Object[] { this.model }; + this.view = (ScriptSourceView) ctor.newInstance(args); + } else { + this.view = new PlainSourceView(model); + } + } catch (java.lang.Error err) { + this.view = new PlainSourceView(model); + } catch (Exception e) { + this.view = new PlainSourceView(model); + } + + this.model.setView(this.view); + initUI(); + this.view.addListener(new UnsavedChangesListener() { + @Override + public void onUnsavedChanges(boolean isUnsaved) { + if(filename != null) { + // enable or disable save button depending on unsaved changes + saveBtn.setEnabled(isUnsaved); + } + } + }); + frame.setVisible(true); + } + + private void initUI() { + frame = new JFrame("BeanShell Debug Window: " + filename); + frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + frame.addWindowListener( + new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + doClose(); + } + } + ); + + String[] labels = {"Run", "Clear", "Save","Undo","Redo"}; + JToolBar toolbar = new JToolBar(); + toolbar.setRollover(true); + for (String label : labels) { + JButton b = new JButton(label); + b.setToolTipText(label); + b.addActionListener(this); + toolbar.add(b); + toolbar.addSeparator(); + + // disable save button on start + if (label.equals("Save")) { + b.setEnabled(false); + saveBtn = b; + } + } + + frame.getContentPane().add((JComponent)view, BorderLayout.CENTER); + frame.add(toolbar, BorderLayout.NORTH); + frame.pack(); + frame.setSize(590, 480); + frame.setLocation(300, 200); + frame.setMinimumSize(new Dimension(500, 300)); + } + + private void doClose() { + if (view.isModified()) { + + int result = JOptionPane.showConfirmDialog(frame, + "The script has been modified. Do you want to save the changes?"); + + if (result == JOptionPane.CANCEL_OPTION) { + // don't close the window, just return + return; + } else if (result == JOptionPane.YES_OPTION) { + boolean saveSuccess = saveTextArea(); + + if (!saveSuccess) { + return; + } + } + } + + frame.dispose(); + shutdown(); + } + + private boolean saveTextArea() { + boolean result = true; + + if (!view.isModified()) { + return true; + } + + OutputStream fos = null; + + try { + String s = view.getText(); + fos = scriptURL.openConnection().getOutputStream(); + + if (fos != null) { + fos.write(s.getBytes()); + } else { + showErrorMessage( + "Error saving script: Could not open stream for file"); + result = false; + } + + view.setModified(false); + } catch (IOException ioe) { + showErrorMessage("Error saving script: " + ioe.getMessage()); + result = false; + } catch (Exception e) { + showErrorMessage("Error saving script: " + e.getMessage()); + result = false; + } finally { + if (fos != null) { + try { + fos.flush(); + } catch (IOException ignore) { + } + try { + fos.close(); + } catch (IOException ignore) { + } + } + } + + return result; + } + + private void shutdown() { + synchronized (BEING_EDITED) { + BEING_EDITED.remove(scriptURL.toExternalForm()); + } + } + + public void actionPerformed(ActionEvent e) { + String actionCommand = e.getActionCommand(); + if (actionCommand.equals("Run")) { + try { + execute(); + } catch (Exception invokeException) { + showErrorMessage(invokeException.toString()); + } + } else if (actionCommand.equals("Save")) { + saveTextArea(); + } else if (actionCommand.equals("Clear")) { + view.clear(); + } else if(actionCommand.equals("Undo")){ + view.undo(); + } else if(actionCommand.equals("Redo")){ + view.redo(); + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell.java new file mode 100644 index 000000000..2aabba712 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptProviderForBeanShell.java @@ -0,0 +1,367 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.beanshell; + +import bsh.Interpreter; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.reflection.InvocationTargetException; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.ScriptErrorRaisedException; +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; + +import com.sun.star.uno.Any; +import com.sun.star.uno.Type; +import com.sun.star.uno.XComponentContext; + +import java.net.URL; + +import java.util.StringTokenizer; + +public class ScriptProviderForBeanShell { + + public static class ScriptProviderForBeanShell_2 extends ScriptProvider { + + public ScriptProviderForBeanShell_2(XComponentContext ctx) { + super(ctx, "BeanShell"); + } + + @Override + public XScript getScript(/*IN*/String scriptURI) throws + com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException { + + ScriptMetaData scriptData = getScriptData(scriptURI); + + try { + + ScriptImpl script = + new ScriptImpl(m_xContext, scriptData, m_xModel, m_xInvocContext); + + return script; + } catch (com.sun.star.uno.RuntimeException re) { + throw new ScriptFrameworkErrorException( + "Failed to create script object: " + re.getMessage(), + null, scriptData.getLanguageName(), language, + ScriptFrameworkErrorType.UNKNOWN); + } + } + + @Override + public boolean hasScriptEditor() { + return true; + } + + @Override + public ScriptEditor getScriptEditor() { + return ScriptEditorForBeanShell.getEditor(); + } + } + + /** + * Returns a factory for creating the service. + * This method is called by the <code>JavaLoader</code> + * <p> + * + * @param implName the name of the implementation for which a service is desired + * @param multiFactory the service manager to be used if needed + * @param regKey the registryKey + * @return returns a <code>XSingleServiceFactory</code> for creating + * the component + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) { + + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals( + ScriptProviderForBeanShell.ScriptProviderForBeanShell_2.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory( + ScriptProviderForBeanShell.ScriptProviderForBeanShell_2.class, + "com.sun.star.script.provider.ScriptProviderForBeanShell", + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } +} + +class ScriptImpl implements XScript { + + private final ScriptMetaData metaData; + private final XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private final XModel m_xModel; + private final XScriptInvocationContext m_xInvocContext; + + ScriptImpl(XComponentContext ctx, ScriptMetaData metaData, XModel xModel, + XScriptInvocationContext xContext) throws + com.sun.star.uno.RuntimeException { + + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xContext; + + try { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } catch (Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + + LogUtils.DEBUG("ScriptImpl [beanshell] script data = " + metaData); + } + + /** + * documentStorageID and document reference + * for use in script name resolving + * + * @param aParams All parameters; pure, out params are + * undefined in sequence, i.e., the value + * has to be ignored by the callee + * + * @param aOutParamIndex Out indices + * + * @param aOutParam Out parameters + * + * @return The value returned from the function + * being invoked + * + * @throws IllegalArgumentException If there is no matching script name + * + * @throws InvocationTargetException If the running script throws + * an exception this information + * is captured and rethrown as + * this exception type. + */ + + public Object invoke(/*IN*/Object[] aParams, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam) throws + ScriptFrameworkErrorException, InvocationTargetException { + + // Initialise the out parameters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + ClassLoader cl = null; + URL sourceUrl = null; + + try { + cl = ClassLoaderFactory.getURLClassLoader(metaData); + sourceUrl = metaData.getSourceURL(); + } catch (java.net.MalformedURLException mfu) { + // Framework error + throw new ScriptFrameworkErrorException( + mfu.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.MALFORMED_URL); + } + + // Set class loader to be used for class files + // and jar files + Thread.currentThread().setContextClassLoader(cl); + Interpreter interpreter = new Interpreter(); + + interpreter.getNameSpace().clear(); + // Set class loader to be used by interpreter + // to look for classes by source e.g. interpreter + // will use this classloader to search classpath + // for source file ( bla.java ) on import or reference + interpreter.setClassLoader(cl); + + try { + + interpreter.set("XSCRIPTCONTEXT", + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory)); + + interpreter.set("ARGUMENTS", aParams); + } catch (bsh.EvalError e) { + // Framework error setting up context + throw new ScriptFrameworkErrorException( + e.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN); + } + + try { + Object result; + + ScriptEditorForBeanShell editor = + ScriptEditorForBeanShell.getEditor(sourceUrl); + + if (editor != null) { + result = editor.execute(); + + if (result == null) { + return new Any(new Type(), null); + } + + return result; + } + + metaData.loadSource(); + String source = metaData.getSource(); + + if (source == null || source.length() == 0) { + throw new ScriptFrameworkErrorException( + "Failed to read script", null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + } + + result = interpreter.eval(source); + + if (result == null) { + return new Any(new Type(), null); + } + + return result; + } catch (bsh.ParseException pe) { + throw new InvocationTargetException( + "Beanshell failed to parse " + metaData.getLanguageName(), + null, processBshException(pe, metaData.getLanguageName())); + } catch (bsh.TargetError te) { + throw new InvocationTargetException( + "Beanshell uncaught exception for " + metaData.getLanguageName(), + null, processBshException(te, metaData.getLanguageName())); + } catch (bsh.EvalError ex) { + throw new InvocationTargetException( + "Beanshell error for " + metaData.getLanguageName(), + null, processBshException(ex, metaData.getLanguageName())); + } catch (Exception e) { + throw new ScriptFrameworkErrorException( + "Failed to read script", null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + } + } + private void raiseEditor(int lineNum) { + try { + URL sourceUrl = metaData.getSourceURL(); + + ScriptEditorForBeanShell editor = + ScriptEditorForBeanShell.getEditor(sourceUrl); + + if (editor == null) { + editor = ScriptEditorForBeanShell.getEditor(); + + editor.edit( + ScriptContext.createContext(m_xModel, m_xInvocContext, m_xContext, + m_xMultiComponentFactory), metaData); + + editor = ScriptEditorForBeanShell.getEditor(sourceUrl); + } + + if (editor != null) { + editor.indicateErrorLine(lineNum); + } + } catch (java.net.MalformedURLException ignore) { + } + } + + private ScriptErrorRaisedException processBshException( + bsh.EvalError e, String script) { + + LogUtils.DEBUG("Beanshell error RAW message " + e.getMessage()); + String message = e.getMessage(); + int usefulInfoIndex = message.lastIndexOf("\' :"); + int lineNum = e.getErrorLineNumber(); + + raiseEditor(lineNum); + + if (usefulInfoIndex > -1) { + message = message.substring(usefulInfoIndex + 2); + } + + if (e instanceof bsh.TargetError) { + LogUtils.DEBUG("got instance of TargetError"); + + if (usefulInfoIndex == -1) { + message = ((bsh.TargetError)e).getTarget().getMessage(); + } + + String wrappedException = ""; + String full = e.toString(); + int index = full.indexOf("Target exception:"); + + if (index > -1) { + String toParse = full.substring(index); + LogUtils.DEBUG("About to parse " + toParse); + + StringTokenizer tokenizer = + new StringTokenizer(full.substring(index), ":"); + + if (tokenizer.countTokens() > 2) { + LogUtils.DEBUG("First token = " + tokenizer.nextToken()); + wrappedException = tokenizer.nextToken(); + LogUtils.DEBUG("wrapped exception = = " + wrappedException); + } + } + + ScriptExceptionRaisedException se = + new ScriptExceptionRaisedException(message); + + se.lineNum = lineNum; + se.scriptName = script; + se.exceptionType = wrappedException; + se.language = "BeanShell"; + + LogUtils.DEBUG("UnCaught Exception error: "); + LogUtils.DEBUG("\tscript: " + script); + LogUtils.DEBUG("\tline: " + lineNum); + LogUtils.DEBUG("\twrapped exception: " + wrappedException); + LogUtils.DEBUG("\tmessage: " + message); + + return se; + + } else { + LogUtils.DEBUG("Error or ParseError Exception error: "); + LogUtils.DEBUG("\tscript: " + script); + LogUtils.DEBUG("\tline: " + lineNum); + LogUtils.DEBUG("\tmessage: " + message); + + return new ScriptErrorRaisedException(message, null, script, + "BeanShell", lineNum); + + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel.java new file mode 100644 index 000000000..165f00844 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceModel.java @@ -0,0 +1,125 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.beanshell; + +import com.sun.star.script.provider.XScriptContext; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; + +public class ScriptSourceModel { + + private int currentPosition = -1; + private final URL file; + private ScriptSourceView view = null; + + public ScriptSourceModel(URL file) { + this.file = file; + } + + private String load() throws IOException { + StringBuilder buf = new StringBuilder(); + InputStream in = file.openStream(); + + byte[] contents = new byte[1024]; + int len; + + while ((len = in.read(contents, 0, 1024)) != -1) { + buf.append(new String(contents, 0, len)); + } + + try { + in.close(); + } catch (IOException ignore) { + } + + return buf.toString(); + } + + public String getText() { + String result = ""; + + try { + result = load(); + } catch (IOException ioe) { + // do nothing, empty string will be returned + } + + return result; + } + + public int getCurrentPosition() { + return this.currentPosition; + } + + public void setView(ScriptSourceView view) { + this.view = view; + } + + public Object execute(final XScriptContext context, ClassLoader cl) + throws Exception { + if (cl != null) { + // sets this threads class loader + // hopefully any threads spawned by this + // will inherit this cl + // this enables any class files imported + // from the interpreter to be loaded + // note: setting the classloader on the + // interpreter has a slightly different + // meaning in that the classloader for + // the interpreter seems only to look for + // source files ( bla.java ) in the classpath + Thread.currentThread().setContextClassLoader(cl); + } + + bsh.Interpreter interpreter = new bsh.Interpreter(); + + if (cl != null) { + // additionally set class loader on the interpreter + // to allow it to load java classes defined in source + // files e.g. bla.java + interpreter.getNameSpace().clear(); + } + + + // reset position + currentPosition = -1; + view.update(); + + interpreter.set("XSCRIPTCONTEXT", context); + interpreter.set("ARGUMENTS", new Object[0]); + + Object result; + + if (view.isModified()) { + result = interpreter.eval(view.getText()); + } else { + result = interpreter.eval(getText()); + } + + return result; + } + + public void indicateErrorLine(int lineNum) { + System.out.println("Beanshell indicateErrorLine " + lineNum); + currentPosition = lineNum - 1; + view.update(); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView.java new file mode 100644 index 000000000..e39511c24 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/ScriptSourceView.java @@ -0,0 +1,29 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.beanshell; + +public interface ScriptSourceView { + void clear(); + void update(); + boolean isModified(); + void setModified(boolean value); + String getText(); + void undo(); + void redo(); + void addListener(UnsavedChangesListener toAdd); +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener.java b/scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener.java new file mode 100644 index 000000000..8efb2087c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/UnsavedChangesListener.java @@ -0,0 +1,13 @@ +/* -*- 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/. + */ +package com.sun.star.script.framework.provider.beanshell; + +public interface UnsavedChangesListener { + void onUnsavedChanges(boolean isModified); +} diff --git a/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh b/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh new file mode 100644 index 000000000..3bf6d22a3 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/beanshell/template.bsh @@ -0,0 +1,65 @@ +/* + * 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 standard OpenOffice.org API classes. For more information on + these classes and the OpenOffice.org API, see the OpenOffice.org + Developers Guide at: + + https://api.libreoffice.org/ +*/ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XModel; + +/* + Import XScriptContext class. An instance of this class is available + to all BeanShell scripts in the global variable "XSCRIPTCONTEXT". This + variable can be used to access the document for which this script + was invoked. + + Methods available are: + + XSCRIPTCONTEXT.getDocument() returns XModel + XSCRIPTCONTEXT.getInvocationContext() returns XScriptInvocationContext or NULL + XSCRIPTCONTEXT.getDesktop() returns XDesktop + XSCRIPTCONTEXT.getComponentContext() returns XComponentContext + + For more information on using this class see the scripting + developer guides at: + + https://api.libreoffice.org/docs/DevelopersGuide/ScriptingFramework/ScriptingFramework.xhtml +*/ + +// Hello World in BeanShell +import com.sun.star.text.XTextDocument; +import com.sun.star.text.XText; +import com.sun.star.text.XTextRange; + +oDoc = UnoRuntime.queryInterface(XModel.class,XSCRIPTCONTEXT.getInvocationContext()); +if ( oDoc == null ) + oDoc = XSCRIPTCONTEXT.getDocument(); + +xTextDoc = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class,oDoc); +xText = xTextDoc.getText(); +xTextRange = xText.getEnd(); +xTextRange.setString( "Hello World (in BeanShell)" ); + +// BeanShell scripts in LibreOffice should always return 0 +return 0; diff --git a/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF b/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF new file mode 100644 index 000000000..f2056d0c4 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.provider.java.ScriptProviderForJava +UNO-Type-Path: diff --git a/scripting/java/com/sun/star/script/framework/provider/java/Resolver.java b/scripting/java/com/sun/star/script/framework/provider/java/Resolver.java new file mode 100644 index 000000000..8192f4724 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/Resolver.java @@ -0,0 +1,36 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.java; + +/** + * The Resolver interface is an interface common to all classes which + * want to implement an algorithm for obtaining a ScriptProxy object + * for a particular ScriptDescriptor and Class + */ +public interface Resolver { + + /** + * Returns a ScriptProxy object for the given ScriptDescriptor and Class + * + * @param sd A script Descriptor + * @param c A Class + * @return The ScriptProxy value + */ + ScriptProxy getProxy(ScriptDescriptor sd, Class<?> c) throws + NoSuchMethodException; +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor.java b/scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor.java new file mode 100644 index 000000000..ff56564b2 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/ScriptDescriptor.java @@ -0,0 +1,165 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.java; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * The <code>ScriptDescriptor</code> object is used to store the search + * criteria that should be used for finding a particular script + */ +public class ScriptDescriptor { + + private final String m_name; + private final String m_methodName; + private final String m_className; + private List<String> m_classpath; + private final ArrayList<Class<?>> m_argumentTypes = new ArrayList<Class<?>>(11); + + /** + * Constructs a ScriptDescriptor for the given name + * + * @param name Script Name + * @exception IllegalArgumentException if the given name does not contain a "." + */ + public ScriptDescriptor(String name) throws IllegalArgumentException { + int idx = name.lastIndexOf('.'); + + if (idx == -1) { + throw new IllegalArgumentException("Invalid method name"); + } + + this.m_name = name; + this.m_methodName = name.substring(idx + 1); + this.m_className = name.substring(0, idx); + } + + /** + * Gets the fully qualified name of this <code>ScriptDescriptor</code> + * + * @return The Script Name value + */ + public String getName() { + return m_name; + } + + /** + * Gets the fully qualified name of this <code>ScriptDescriptor</code> + * + * @return The Script Name value + */ + public String getClassName() { + return m_className; + } + + /** + * Gets the method name of this <code>ScriptDescriptor</code> + * + * @return The methodName value + */ + public String getMethodName() { + return m_methodName; + } + + /** + * Sets the classpath value stored by this <code>ScriptDescriptor</code> + * + * @param classpath The new classpath value + */ + public void setClasspath(String classpath) { + StringTokenizer stk = new StringTokenizer(classpath, ":"); + + while (stk.hasMoreTokens()) { + this.m_classpath.add(stk.nextToken()); + } + } + + /** + * Sets the classpath value stored by this <code>ScriptDescriptor</code> + * + * @param classpath The new classpath value + */ + public void setClasspath(List<String> classpath) { + this.m_classpath = classpath; + } + + /** + * Gets the classpath value stored by this <code>ScriptDescriptor</code> + * + * @return The classpath value + */ + public List<String> getClasspath() { + return m_classpath; + } + + /** + * Adds the given <code>Class</code> to the list of argument types stored in + * this ScriptDescriptor + * + * @param clazz The feature to be added to the ArgumentType attribute + */ + + public synchronized void addArgumentType(Class<?> clazz) { + m_argumentTypes.add(clazz); + } + + /** + * Gets a list of the types of the arguments stored in this + * <code>ScriptDescriptor</code> + * + * return the argument types as an array of Class + * + * @return The argumentTypes value + */ + + public synchronized Class<?>[] getArgumentTypes() { + if (m_argumentTypes.size() > 0) + return m_argumentTypes.toArray(new Class[ m_argumentTypes.size() ]); + else + return null; + } + + /** + * Returns a <code>String</code> representation of this + * <code>ScriptDescriptor</code> + * + * @return The scriptName including the parameters. + */ + @Override + public String toString() { + StringBuilder description = new StringBuilder(m_name); + Class<?>[] types = getArgumentTypes(); + + description.append(" ("); + + if (types != null) { + for (int i = 0; i < types.length - 1; i++) { + description.append(types[ i ].getName()); + description.append(", "); + } + + description.append(types[ types.length - 1 ].getName()); + } + + description.append(")"); + + return description.toString(); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava.java b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava.java new file mode 100644 index 000000000..eeea0d49e --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProviderForJava.java @@ -0,0 +1,307 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.java; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; +import com.sun.star.script.provider.XScriptContext; + +import com.sun.star.uno.Any; +import com.sun.star.uno.Type; +import com.sun.star.uno.XComponentContext; + +import java.util.ArrayList; + +public class ScriptProviderForJava { + + public static class _ScriptProviderForJava extends ScriptProvider { + + private final Resolver m_resolutionPolicy = new StrictResolver(); + + public _ScriptProviderForJava(XComponentContext ctx) { + super(ctx, "Java"); + } + + @Override + public XScript getScript(/*IN*/String scriptURI) throws + com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException { + + ScriptMetaData scriptData = getScriptData(scriptURI); + + try { + + ScriptImpl script = + new ScriptImpl(m_xContext, m_resolutionPolicy, scriptData, m_xModel, + m_xInvocContext); + + return script; + } catch (com.sun.star.uno.RuntimeException re) { + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + "Failed to create script object: " + re, null, + scriptData.getLanguageName(), language, + ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(re); + throw e2; + } + } + + @Override + public boolean hasScriptEditor() { + return false; + } + + @Override + public ScriptEditor getScriptEditor() { + return null; + } + } + + /** + * Returns a factory for creating the service. + * This method is called by the <code>JavaLoader</code> + * <p> + * + * @param implName the name of the implementation for which a service is desired + * @param multiFactory the service manager to be used if needed + * @param regKey the registryKey + * @return returns a <code>XSingleServiceFactory</code> for creating + * the component + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory( + String implName, XMultiServiceFactory multiFactory, XRegistryKey regKey) { + + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals( + ScriptProviderForJava._ScriptProviderForJava.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory( + ScriptProviderForJava._ScriptProviderForJava.class, + "com.sun.star.script.provider.ScriptProviderForJava", + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } +} + +class ScriptImpl implements XScript { + + private final ScriptMetaData metaData; + private final XComponentContext m_xContext; + private final XModel m_xModel; + private final XScriptInvocationContext m_xInvocContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private final Resolver m_resolutionPolicy; + + ScriptImpl(XComponentContext ctx, Resolver resolver, ScriptMetaData metaData, + XModel xModel, XScriptInvocationContext xInvocContext) throws + com.sun.star.uno.RuntimeException { + + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xInvocContext; + this.m_resolutionPolicy = resolver; + + try { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } catch (Exception e) { + LogUtils.DEBUG(LogUtils.getTrace(e)); + throw new com.sun.star.uno.RuntimeException( + e, "Error constructing ScriptProvider: " + e); + } + + LogUtils.DEBUG("ScriptImpl [java] script data = " + metaData); + } + + /** + * Invoke + * + * + * @param params All parameters; pure, out params are undefined in + * sequence, i.e., the value has to be ignored by the callee + * @param aOutParamIndex Out indices + * @param aOutParam Out parameters + * @return The value returned from the function being invoked + * @throws IllegalArgumentException If there is no matching script name + * @throws com.sun.star.reflection.InvocationTargetException If the running script throws an exception + * this information is captured and rethrown as this exception type. + */ + public Object invoke( + /*IN*/Object[] params, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam) throws + ScriptFrameworkErrorException, + com.sun.star.reflection.InvocationTargetException { + + LogUtils.DEBUG("** ScriptProviderForJava::invoke: Starting..."); + + // Initialise the out parameters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + ScriptDescriptor scriptDesc = + new ScriptDescriptor(metaData.getLanguageName()); + + LogUtils.DEBUG("Classloader starting..."); + + ClassLoader scriptLoader = + ClassLoaderFactory.getURLClassLoader(metaData); + + LogUtils.DEBUG("Classloader finished..."); + + ArrayList<Object> invocationArgList = new ArrayList<Object>(); + Object[] invocationArgs = null; + + LogUtils.DEBUG("Parameter Mapping..."); + + // Setup Context Object + XScriptContext xSc = + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory); + + scriptDesc.addArgumentType(XScriptContext.class); + invocationArgList.add(xSc); + + for (int i = 0; i < params.length; i++) { + scriptDesc.addArgumentType(params[ i ].getClass()); + invocationArgList.add(params[ i ]); + } + + if (!invocationArgList.isEmpty()) { + invocationArgs = invocationArgList.toArray(); + } + + LogUtils.DEBUG("ScriptProxy starting... "); + ScriptProxy script = null; + + try { + + String className = metaData.getLanguageName().substring(0, + metaData.getLanguageName().lastIndexOf('.')); + + LogUtils.DEBUG("About to load Class " + className + " starting... "); + + long start = new java.util.Date().getTime(); + Class<?> c = scriptLoader.loadClass(className); + long end = new java.util.Date().getTime(); + + LogUtils.DEBUG("loadClass took: " + (end - start) + "milliseconds"); + + try { + LogUtils.DEBUG("class loaded ... "); + script = m_resolutionPolicy.getProxy(scriptDesc, c); + LogUtils.DEBUG("script resolved ... "); + } catch (NoSuchMethodException e) { + // Framework error + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + e.toString(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(e); + throw e2; + } + } catch (ClassNotFoundException e) { + // Framework error + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + e.toString(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.NO_SUCH_SCRIPT); + e2.initCause(e); + throw e2; + } + + LogUtils.DEBUG("Starting Invoke on Proxy ..."); + Object result = null; + + try { + long start = new java.util.Date().getTime(); + result = script.invoke(invocationArgs); + long end = new java.util.Date().getTime(); + LogUtils.DEBUG("invoke took: " + (end - start) + "milliseconds"); + } catch (java.lang.IllegalArgumentException iae) { + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + iae.getMessage(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(iae); + throw e2; + } catch (java.lang.IllegalAccessException ia) { + ScriptFrameworkErrorException e2 = new ScriptFrameworkErrorException( + ia.toString(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + e2.initCause(ia); + throw e2; + } catch (java.lang.reflect.InvocationTargetException ite) { + Throwable targetException = ite.getTargetException(); + + ScriptExceptionRaisedException se = + new ScriptExceptionRaisedException(targetException.toString()); + + se.lineNum = -1; + se.scriptName = metaData.getLanguageName(); + se.language = "Java"; + se.exceptionType = targetException.getClass().getName(); + + throw new com.sun.star.reflection.InvocationTargetException( + "Scripting Framework error executing script ", null, se); + + } catch (Exception unknown) { + ScriptExceptionRaisedException se = + new ScriptExceptionRaisedException(unknown.toString()); + se.lineNum = -1; + se.scriptName = metaData.getLanguageName(); + se.language = "Java"; + se.exceptionType = unknown.getClass().getName(); + throw new com.sun.star.reflection.InvocationTargetException( + "Scripting Framework error executing script ", null, se); + } + + if (result == null) { + LogUtils.DEBUG("Got Nothing Back"); + // in the case where there is no return type + Any voidAny = new Any(new Type(), null); + result = voidAny; + } else { + LogUtils.DEBUG("Got object " + result); + } + + return result; + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy.java b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy.java new file mode 100644 index 000000000..921b282cc --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/ScriptProxy.java @@ -0,0 +1,69 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.java; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * A ScriptProxy object acts as a proxy for a Java <code>Method</code> + * + * @see java.lang.reflect.Method + */ +public class ScriptProxy { + + private Object m_targetObject; + private final Method m_method; + + /** + * Constructs a <code>ScriptProxy</code> object for the given + * <code>Method</code> + * + * @param method Description of the Parameter + */ + public ScriptProxy(Method method) { + this.m_method = method; + } + + /** + * Sets the <code>Object</code> on which the ScriptProxy should invoke + * the method + * + * @param obj The new targetObject value + */ + public void setTargetObject(Object obj) { + m_targetObject = obj; + } + + /** + * Invokes the method contained in this <code>ScriptProxy</code>, + * any exceptions resulting from the invocation will be thrown + * + * @param args the arguments to be passed when invoking + * the method + * @return the Object returned from the method + * invocation, may be null + * @see java.lang.reflect.Method for the exceptions + * that may be thrown + */ + public Object invoke(Object[] args) throws + IllegalAccessException, InvocationTargetException, IllegalArgumentException { + + return m_method.invoke(m_targetObject, args); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/java/StrictResolver.java b/scripting/java/com/sun/star/script/framework/provider/java/StrictResolver.java new file mode 100644 index 000000000..08d06dce3 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/java/StrictResolver.java @@ -0,0 +1,113 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider.java; + +import com.sun.star.script.framework.log.LogUtils; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * A StrictResolver can be used to get a ScriptProxy object for a given + * ScriptDescriptor and Class. The StrictResolver is an implementation of + * the Resolver strategy. It will only return a ScriptProxy object if a + * method accepting all of the arguments specified in the ScriptDescriptor + * can be found in the Class. + */ +public class StrictResolver implements Resolver { + + /** + *Constructor for the StrictResolver object + */ + public StrictResolver() { + LogUtils.DEBUG(this.getClass().getName() + " created"); + } + + /** + * Returns a ScriptProxy object for the given ScriptDescriptor and Class. + * Only a strict match will be returned ie. where all of the arguments in + * the given ScriptDescriptor match the types of the + * + * @param sd the ScriptDescriptor for which a ScriptProxy is required + * @param c the Class file in which to search for the method + * @return the ScriptProxy matching the criteria, or null if no match is found + */ + public ScriptProxy getProxy(ScriptDescriptor sd, Class<?> c) throws + NoSuchMethodException { + + Method m = null; + + LogUtils.DEBUG("StrictResolver.getProxy() for: " + sd.toString()); + + try { + m = resolveArguments(sd, c); + } catch (ClassNotFoundException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "StrictResolver.getProxy: Can't find method: " + sd.getMethodName()); + ex2.initCause(ex1); + throw ex2; + } catch (NoSuchMethodException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "StrictResolver.getProxy: Can't find method: " + sd.getMethodName()); + ex2.initCause(ex1); + throw ex2; + } + + ScriptProxy sp = new ScriptProxy(m); + + int modifiers = m.getModifiers(); + + if (!Modifier.isStatic(modifiers)) { + Object o; + + try { + o = c.newInstance(); + } catch (InstantiationException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "getScriptProxy: Can't instantiate: " + c.getName()); + ex2.initCause(ex1); + throw ex2; + } catch (IllegalAccessException ex1) { + NoSuchMethodException ex2 = new NoSuchMethodException( + "getScriptProxy: Can't access: " + c.getName()); + ex2.initCause(ex1); + throw ex2; + } + + sp.setTargetObject(o); + } + + return sp; + } + + /** + * Description of the Method + * + * @param sd Description of the Parameter + * @param c Description of the Parameter + * @return Description of the Return Value + * @exception ClassNotFoundException + * @exception NoSuchMethodException + */ + private Method resolveArguments(ScriptDescriptor sd, Class<?> c) throws + ClassNotFoundException, NoSuchMethodException { + + return c.getMethod(sd.getMethodName(), sd.getArgumentTypes()); + } +}
\ No newline at end of file diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF b/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF new file mode 100644 index 000000000..fe2f543c0 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/MANIFEST.MF @@ -0,0 +1,2 @@ +RegistrationClassName: com.sun.star.script.framework.provider.javascript.ScriptProviderForJavaScript +UNO-Type-Path: diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java new file mode 100644 index 000000000..1f0e8fd72 --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptEditorForJavaScript.java @@ -0,0 +1,319 @@ +/* + * 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 . + */ + +package com.sun.star.script.framework.provider.javascript; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ScriptEditorBase; +import com.sun.star.script.framework.provider.SwingInvocation; +import com.sun.star.script.provider.XScriptContext; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import java.io.IOException; +import java.io.InputStream; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tools.debugger.Main; +import org.mozilla.javascript.tools.debugger.ScopeProvider; + +public class ScriptEditorForJavaScript extends ScriptEditorBase { + + // global ScriptEditorForJavaScript instance + private static ScriptEditorForJavaScript theScriptEditorForJavaScript; + + // template for JavaScript scripts + private static String JSTEMPLATE; + + private static Main rhinoWindow; + private URL scriptURL; + // global list of ScriptEditors, key is [external form of URL] of file being edited + private static Map<String, ScriptEditorForJavaScript> BEING_EDITED = new + HashMap<String, ScriptEditorForJavaScript>(); + + static { + JSTEMPLATE = "// JavaScript script"; + try { + URL url = ScriptEditorForJavaScript.class.getResource("template.js"); + if (url != null) { + InputStream in = url.openStream(); + StringBuilder buf = new StringBuilder(); + byte[] b = new byte[1024]; + int len; + + while ((len = in.read(b)) != -1) { + buf.append(new String(b, 0, len)); + } + + in.close(); + + JSTEMPLATE = buf.toString(); + } + } catch (IOException ioe) { + } catch (Exception e) { + } + } + + /** + * Returns the global ScriptEditorForJavaScript instance. + */ + + public static synchronized ScriptEditorForJavaScript getEditor() { + if (theScriptEditorForJavaScript == null) { + theScriptEditorForJavaScript = new ScriptEditorForJavaScript(); + } + + return theScriptEditorForJavaScript; + } + + /** + * Get the ScriptEditorForJavaScript instance for this URL + * + * @param url The URL of the script source file + * + * @return The ScriptEditorForJavaScript associated with + * the given URL if one exists, otherwise null. + */ + public static ScriptEditorForJavaScript getEditor(URL url) { + synchronized (BEING_EDITED) { + return BEING_EDITED.get(url.toExternalForm()); + } + } + + /** + * Returns whether or not the script source being edited in this + * ScriptEditorForJavaScript has been modified + */ + public boolean isModified() { + return rhinoWindow.isModified(scriptURL); + } + + /** + * Returns the text being displayed in this ScriptEditorForJavaScript + * + * @return The text displayed in this ScriptEditorForJavaScript + */ + public String getText() { + return rhinoWindow.getText(scriptURL); + } + + /** + * Returns the Rhino Debugger url of this ScriptEditorForJavaScript + * + * @return The url of this ScriptEditorForJavaScript + */ + public String getURL() { + return scriptURL.toString(); + } + + /** + * Returns the template text for JavaScript scripts + * + * @return The template text for JavaScript scripts + */ + public String getTemplate() { + return JSTEMPLATE; + } + + /** + * Returns the default extension for JavaScript scripts + * + * @return The default extension for JavaScript scripts + */ + public String getExtension() { + return "js"; + } + + /** + * Opens an editor window for the specified ScriptMetaData. + * If an editor window is already open for that data it will be + * moved to the front. + * + * @param context The context in which to execute the script + * @param entry The metadata describing the script + */ + public void edit(final XScriptContext context, ScriptMetaData entry) { + try { + String sUrl = entry.getParcelLocation(); + + if (!sUrl.endsWith("/")) { + sUrl += "/"; + } + + sUrl += entry.getLanguageName(); + final URL url = entry.getSourceURL(); + SwingInvocation.invoke( + new Runnable() { + public void run() { + synchronized (BEING_EDITED) { + ScriptEditorForJavaScript editor = BEING_EDITED.get(url.toExternalForm()); + + if (editor == null) { + editor = new ScriptEditorForJavaScript(context, url); + BEING_EDITED.put(url.toExternalForm(), editor); + } + } + + assert rhinoWindow != null; + rhinoWindow.showScriptWindow(url); + rhinoWindow.toFront(); + } + }); + } catch (IOException e) { + LogUtils.DEBUG("Caught exception: " + e); + LogUtils.DEBUG(LogUtils.getTrace(e)); + } + } + + // Ensures that new instances of this class can only be created using + // the factory methods + private ScriptEditorForJavaScript() { + } + + private ScriptEditorForJavaScript(XScriptContext context, URL url) { + setContext(context); + // Need to check that before showing the window. Checking in execute() has no effect. + if (!isMacroExecutionEnabled()) { + showErrorMessage("Macro Execution has been disabled."); + return ; + } + initUI(); + Scriptable scope = getScope(context); + rhinoWindow.openFile(url, scope, new CloseHandler(url)); + this.scriptURL = url; + } + + /** + * Executes the script edited by the editor + * + */ + + public Object execute() throws Exception { + rhinoWindow.toFront(); + + return rhinoWindow.runScriptWindow(scriptURL); + } + + /** + * Indicates the line where error occurred + * + */ + public void indicateErrorLine(int lineNum) { + rhinoWindow.toFront(); + rhinoWindow.highlighLineInScriptWindow(scriptURL, lineNum); + } + // This code is based on the main method of the Rhino Debugger Main class + // We pass in the XScriptContext in the global scope for script execution + private void initUI() { + try { + synchronized (ScriptEditorForJavaScript.class) { + if (rhinoWindow != null) { + return; + } + + final Main sdb = new Main("Rhino JavaScript Debugger"); + sdb.pack(); + sdb.setSize(640, 640); + sdb.setVisible(true); + sdb.setExitAction(new Runnable() { + public void run() { + sdb.clearAllBreakpoints(); + sdb.dispose(); + shutdown(); + } + }); + Context.addContextListener(sdb); + sdb.setScopeProvider(new ScopeProvider() { + @Override + public Scriptable getScope() { + return org.mozilla.javascript.tools.shell.Main.getScope(); + } + }); + sdb.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + shutdown(); + } + }); + rhinoWindow = sdb; + } + } catch (Exception exc) { + LogUtils.DEBUG(LogUtils.getTrace(exc)); + } + } + + private void shutdown() { + // dereference Rhino Debugger window + rhinoWindow = null; + this.scriptURL = null; + + // remove all scripts from BEING_EDITED + synchronized (BEING_EDITED) { + java.util.Iterator<String> iter = BEING_EDITED.keySet().iterator(); + java.util.ArrayList<String> keysToRemove = new java.util.ArrayList<String>(); + + while (iter.hasNext()) { + String key = iter.next(); + keysToRemove.add(key); + } + + for (int i = 0; i < keysToRemove.size(); i++) { + BEING_EDITED.remove(keysToRemove.get(i)); + } + + keysToRemove = null; + } + + } + private Scriptable getScope(XScriptContext xsctxt) { + Context ctxt = Context.enter(); + ImporterTopLevel scope = new ImporterTopLevel(ctxt); + + Scriptable jsCtxt = Context.toObject(xsctxt, scope); + scope.put("XSCRIPTCONTEXT", scope, jsCtxt); + + Scriptable jsArgs = Context.toObject(new Object[0], scope); + scope.put("ARGUMENTS", scope, jsArgs); + + Context.exit(); + return scope; + } + + private static class CloseHandler implements Runnable { + + private final URL url; + + private CloseHandler(URL url) { + this.url = url; + } + + public void run() { + synchronized (BEING_EDITED) { + BEING_EDITED.remove(this.url.toExternalForm()); + } + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java new file mode 100644 index 000000000..9465a646f --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/ScriptProviderForJavaScript.java @@ -0,0 +1,330 @@ +/* + * 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 . + */ +package com.sun.star.script.framework.provider.javascript; + +import com.sun.star.comp.loader.FactoryHelper; + +import com.sun.star.document.XScriptInvocationContext; + +import com.sun.star.frame.XModel; + +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; + +import com.sun.star.reflection.InvocationTargetException; + +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.script.framework.container.ScriptMetaData; +import com.sun.star.script.framework.log.LogUtils; +import com.sun.star.script.framework.provider.ClassLoaderFactory; +import com.sun.star.script.framework.provider.ScriptContext; +import com.sun.star.script.framework.provider.ScriptEditor; +import com.sun.star.script.framework.provider.ScriptProvider; +import com.sun.star.script.provider.ScriptExceptionRaisedException; +import com.sun.star.script.provider.ScriptFrameworkErrorException; +import com.sun.star.script.provider.ScriptFrameworkErrorType; +import com.sun.star.script.provider.XScript; + +import com.sun.star.uno.XComponentContext; + +import java.net.URL; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ImporterTopLevel; +import org.mozilla.javascript.JavaScriptException; +import org.mozilla.javascript.Scriptable; + +public class ScriptProviderForJavaScript { + + public static class ScriptProviderForJavaScript_2 extends ScriptProvider { + + public ScriptProviderForJavaScript_2(XComponentContext ctx) { + super(ctx, "JavaScript"); + } + + @Override + public XScript getScript(/*IN*/String scriptURI) + throws com.sun.star.uno.RuntimeException, ScriptFrameworkErrorException { + ScriptMetaData scriptData = null; + + try { + scriptData = getScriptData(scriptURI); + ScriptImpl script = new ScriptImpl(m_xContext, scriptData, m_xModel, + m_xInvocContext); + return script; + } catch (com.sun.star.uno.RuntimeException re) { + throw new ScriptFrameworkErrorException( + "Failed to create script object: " + re.getMessage(), + null, scriptData.getLanguageName(), language, ScriptFrameworkErrorType.UNKNOWN); + } + } + + @Override + public boolean hasScriptEditor() { + return true; + } + + @Override + public ScriptEditor getScriptEditor() { + return ScriptEditorForJavaScript.getEditor(); + } + } + + /** + * Returns a factory for creating the service. + * This method is called by the <code>JavaLoader</code> + * <p> + * + * @param implName the name of the implementation for which a service is desired + * @param multiFactory the service manager to be used if needed + * @param regKey the registryKey + * @return returns a <code>XSingleServiceFactory</code> for creating + * the component + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, XRegistryKey regKey) { + + XSingleServiceFactory xSingleServiceFactory = null; + + if (implName.equals( + ScriptProviderForJavaScript.ScriptProviderForJavaScript_2.class.getName())) { + + xSingleServiceFactory = + FactoryHelper.getServiceFactory( + ScriptProviderForJavaScript.ScriptProviderForJavaScript_2.class, + "com.sun.star.script.provider.ScriptProviderForJavaScript", + multiFactory, regKey); + + } + + return xSingleServiceFactory; + } +} + +class ScriptImpl implements XScript { + + private final ScriptMetaData metaData; + private final XComponentContext m_xContext; + private XMultiComponentFactory m_xMultiComponentFactory; + private final XModel m_xModel; + private final XScriptInvocationContext m_xInvocContext; + + ScriptImpl(XComponentContext ctx, ScriptMetaData metaData, XModel xModel, + XScriptInvocationContext xInvocContext) throws + com.sun.star.uno.RuntimeException { + + this.metaData = metaData; + this.m_xContext = ctx; + this.m_xModel = xModel; + this.m_xInvocContext = xInvocContext; + + try { + this.m_xMultiComponentFactory = m_xContext.getServiceManager(); + } catch (Exception e) { + throw new com.sun.star.uno.RuntimeException(e); + } + + LogUtils.DEBUG("ScriptImpl [javascript] script data = " + metaData); + } + + /** + * The invoke method of the ScriptProviderForJavaScript runs the + * JavaScript script specified in the URI + * + * + * + * @param params All parameters; pure, out params are + * undefined in sequence, i.e., the value + * has to be ignored by the callee + * + * @param aOutParamIndex Out indices + * + * @param aOutParam Out parameters + * + * @return The value returned from the function + * being invoked + * + * @throws ScriptFrameworkErrorException If there is no matching script name + * + * + * @throws InvocationTargetException If the running script throws + * an exception this information + * is captured and rethrown as + * ScriptErrorRaisedException or + * ScriptExceptionRaisedException + */ + public Object invoke( + /*IN*/Object[] params, + /*OUT*/short[][] aOutParamIndex, + /*OUT*/Object[][] aOutParam) + throws ScriptFrameworkErrorException, InvocationTargetException { + + // Initialise the out parameters - not used at the moment + aOutParamIndex[0] = new short[0]; + aOutParam[0] = new Object[0]; + + ClassLoader cl = null; + + try { + cl = ClassLoaderFactory.getURLClassLoader(metaData); + metaData.getSourceURL(); + } catch (java.net.MalformedURLException mfu) { + throw new ScriptFrameworkErrorException( + mfu.getMessage(), null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.MALFORMED_URL); + } + + Context ctxt = null; + + try { + Object result = null; + + ScriptEditorForJavaScript editor = + ScriptEditorForJavaScript.getEditor(metaData.getSourceURL()); + + if (editor != null) { + result = editor.execute(); + + if (result != null && + result.getClass().getName().equals("org.mozilla.javascript.Undefined")) { + // Always return a string + // TODO revisit + return Context.toString(result); + } + + } + + String source; + + if (editor != null && editor.isModified()) { + LogUtils.DEBUG("GOT A MODIFIED SOURCE"); + source = editor.getText(); + } else { + metaData.loadSource(); + source = metaData.getSource(); + + } + + if (source == null || source.length() == 0) { + throw new ScriptFrameworkErrorException( + "Failed to read source data for script", null, + metaData.getLanguageName(), metaData.getLanguage(), + ScriptFrameworkErrorType.UNKNOWN); + } + + /* Set the context ClassLoader on the current thread to + be our custom ClassLoader. This is the suggested method + for setting up a ClassLoader to be used by the Rhino + interpreter + */ + if (cl != null) { + Thread.currentThread().setContextClassLoader(cl); + } + + // Initialize a Rhino Context object + ctxt = Context.enter(); + + /* The ImporterTopLevel ensures that importClass and + importPackage statements work in Javascript scripts + Make the XScriptContext available as a global variable + to the script + */ + ImporterTopLevel scope = new ImporterTopLevel(ctxt); + + Scriptable jsCtxt = + Context.toObject( + ScriptContext.createContext( + m_xModel, m_xInvocContext, m_xContext, + m_xMultiComponentFactory), + scope); + + scope.put("XSCRIPTCONTEXT", scope, jsCtxt); + + Scriptable jsArgs = Context.toObject(params, scope); + scope.put("ARGUMENTS", scope, jsArgs); + + result = ctxt.evaluateString(scope, source, "<stdin>", 1, null); + result = Context.toString(result); + return result; + } catch (JavaScriptException jse) { + LogUtils.DEBUG("Caught JavaScriptException exception for JavaScript type = " + + jse.getClass()); + String message = jse.getMessage(); + Object wrap = jse.getValue(); + LogUtils.DEBUG("\t message " + message); + LogUtils.DEBUG("\t wrapped type " + wrap.getClass()); + LogUtils.DEBUG("\t wrapped toString " + wrap.toString()); + ScriptExceptionRaisedException se = new + ScriptExceptionRaisedException(message); + se.lineNum = -1; + se.language = "JavaScript"; + se.scriptName = metaData.getLanguageName(); + se.exceptionType = wrap.getClass().getName(); + se.language = metaData.getLanguage(); + LogUtils.DEBUG("ExceptionRaised exception "); + LogUtils.DEBUG("\t message " + se.getMessage()); + LogUtils.DEBUG("\t lineNum " + se.lineNum); + LogUtils.DEBUG("\t language " + se.language); + LogUtils.DEBUG("\t scriptName " + se.scriptName); + raiseEditor(se.lineNum); + throw new InvocationTargetException( + "JavaScript uncaught exception" + metaData.getLanguageName(), null, se); + } catch (Exception ex) { + LogUtils.DEBUG("Caught Exception " + ex); + LogUtils.DEBUG("rethrowing as ScriptFramework error"); + throw new ScriptFrameworkErrorException( + ex.getMessage(), null, metaData.getLanguageName(), + metaData.getLanguage(), ScriptFrameworkErrorType.UNKNOWN); + } finally { + if (ctxt != null) { + Context.exit(); + } + } + } + + private void raiseEditor(int lineNum) { + try { + URL sourceUrl = metaData.getSourceURL(); + + ScriptEditorForJavaScript editor = + ScriptEditorForJavaScript.getEditor(sourceUrl); + + if (editor == null) { + editor = ScriptEditorForJavaScript.getEditor(); + + editor.edit( + ScriptContext.createContext(m_xModel, m_xInvocContext, + m_xContext, m_xMultiComponentFactory), + metaData); + + editor = ScriptEditorForJavaScript.getEditor(sourceUrl); + } + + if (editor != null) { + System.out.println("** Have raised IDE for JavaScript, calling indicateErrorLine for line " + + lineNum); + editor.indicateErrorLine(lineNum); + } + } catch (java.net.MalformedURLException ignore) { + } + } +} diff --git a/scripting/java/com/sun/star/script/framework/provider/javascript/template.js b/scripting/java/com/sun/star/script/framework/provider/javascript/template.js new file mode 100644 index 000000000..939ba339c --- /dev/null +++ b/scripting/java/com/sun/star/script/framework/provider/javascript/template.js @@ -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 . + */ +// Hello World in JavaScript +// Import standard OpenOffice.org API classes. For more information on +// these classes and the OpenOffice.org API, see the OpenOffice.org +// Developers Guide at: +// https://api.libreoffice.org/ + +importClass(Packages.com.sun.star.uno.UnoRuntime); +importClass(Packages.com.sun.star.text.XTextDocument); +importClass(Packages.com.sun.star.text.XText); +importClass(Packages.com.sun.star.text.XTextRange); +importClass(Packages.com.sun.star.frame.XModel); + +// Import XScriptContext class. An instance of this class is available +// to all JavaScript scripts in the global variable "XSCRIPTCONTEXT". This +// variable can be used to access the document for which this script +// was invoked. + +// Methods available are: + +// XSCRIPTCONTEXT.getDocument() returns XModel +// XSCRIPTCONTEXT.getInvocationContext() returns XScriptInvocationContext or NULL +// XSCRIPTCONTEXT.getDesktop() returns XDesktop +// XSCRIPTCONTEXT.getComponentContext() returns XComponentContext + +// For more information on using this class see the scripting +// developer guides at: + +// https://api.libreoffice.org/docs/DevelopersGuide/ScriptingFramework/ScriptingFramework.xhtml + + +oDoc = UnoRuntime.queryInterface(XModel,XSCRIPTCONTEXT.getInvocationContext()); +if ( !oDoc ) + oDoc = XSCRIPTCONTEXT.getDocument(); +xTextDoc = UnoRuntime.queryInterface(XTextDocument,oDoc); +xText = xTextDoc.getText(); +xTextRange = xText.getEnd(); +xTextRange.setString( "Hello World (in JavaScript)" ); diff --git a/scripting/source/basprov/baslibnode.cxx b/scripting/source/basprov/baslibnode.cxx new file mode 100644 index 000000000..33942c6b2 --- /dev/null +++ b/scripting/source/basprov/baslibnode.cxx @@ -0,0 +1,130 @@ +/* -*- 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 "baslibnode.hxx" +#include "basmodnode.hxx" +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> +#include <comphelper/sequence.hxx> +#include <vcl/svapp.hxx> +#include <basic/basmgr.hxx> +#include <basic/sbstar.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; + + +namespace basprov +{ + + + // BasicLibraryNodeImpl + + + BasicLibraryNodeImpl::BasicLibraryNodeImpl( const Reference< XComponentContext >& rxContext, + const OUString& sScriptingContext, BasicManager* pBasicManager, + const Reference< script::XLibraryContainer >& xLibContainer, const OUString& sLibName, bool isAppScript ) + :m_xContext( rxContext ) + ,m_sScriptingContext( sScriptingContext ) + ,m_pBasicManager( pBasicManager ) + ,m_xLibContainer( xLibContainer ) + ,m_sLibName( sLibName ) + ,m_bIsAppScript( isAppScript ) + { + if ( m_xLibContainer.is() ) + { + Any aElement = m_xLibContainer->getByName( m_sLibName ); + aElement >>= m_xLibrary; + } + } + + + BasicLibraryNodeImpl::~BasicLibraryNodeImpl() + { + } + + + // XBrowseNode + + + OUString BasicLibraryNodeImpl::getName( ) + { + SolarMutexGuard aGuard; + + return m_sLibName; + } + + + Sequence< Reference< browse::XBrowseNode > > BasicLibraryNodeImpl::getChildNodes( ) + { + SolarMutexGuard aGuard; + + std::vector< Reference< browse::XBrowseNode > > aChildNodes; + + if ( m_xLibContainer.is() && m_xLibContainer->hasByName( m_sLibName ) && !m_xLibContainer->isLibraryLoaded( m_sLibName ) ) + m_xLibContainer->loadLibrary( m_sLibName ); + + if ( m_pBasicManager ) + { + StarBASIC* pBasic = m_pBasicManager->GetLib( m_sLibName ); + if ( pBasic && m_xLibrary.is() ) + { + Sequence< OUString > aNames = m_xLibrary->getElementNames(); + sal_Int32 nCount = aNames.getLength(); + const OUString* pNames = aNames.getConstArray(); + aChildNodes.resize( nCount ); + + for ( sal_Int32 i = 0 ; i < nCount ; ++i ) + { + SbModule* pModule = pBasic->FindModule( pNames[i] ); + if ( pModule ) + aChildNodes[i] = new BasicModuleNodeImpl(m_xContext, m_sScriptingContext, + pModule, m_bIsAppScript); + } + } + } + + return comphelper::containerToSequence(aChildNodes); + } + + + sal_Bool BasicLibraryNodeImpl::hasChildNodes( ) + { + SolarMutexGuard aGuard; + + bool bReturn = false; + if ( m_xLibrary.is() ) + bReturn = m_xLibrary->hasElements(); + + return bReturn; + } + + + sal_Int16 BasicLibraryNodeImpl::getType( ) + { + return browse::BrowseNodeTypes::CONTAINER; + } + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/baslibnode.hxx b/scripting/source/basprov/baslibnode.hxx new file mode 100644 index 000000000..46d31aae5 --- /dev/null +++ b/scripting/source/basprov/baslibnode.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/browse/XBrowseNode.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +class BasicManager; + + +namespace basprov +{ + + + + + typedef ::cppu::WeakImplHelper< + css::script::browse::XBrowseNode > BasicLibraryNodeImpl_BASE; + + + class BasicLibraryNodeImpl : public BasicLibraryNodeImpl_BASE + { + private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + OUString m_sScriptingContext; + BasicManager* m_pBasicManager; + css::uno::Reference< css::script::XLibraryContainer > m_xLibContainer; + css::uno::Reference< css::container::XNameContainer > m_xLibrary; + OUString m_sLibName; + bool m_bIsAppScript; + + public: + BasicLibraryNodeImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sScriptingContext, + BasicManager* pBasicManager, + const css::uno::Reference< css::script::XLibraryContainer >& xLibContainer, + const OUString& sLibName, bool isAppScript ); + virtual ~BasicLibraryNodeImpl() override; + + // XBrowseNode + virtual OUString SAL_CALL getName( ) override; + virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override; + virtual sal_Bool SAL_CALL hasChildNodes( ) override; + virtual sal_Int16 SAL_CALL getType( ) override; + }; + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basmethnode.cxx b/scripting/source/basprov/basmethnode.cxx new file mode 100644 index 000000000..fca2cafe8 --- /dev/null +++ b/scripting/source/basprov/basmethnode.cxx @@ -0,0 +1,291 @@ +/* -*- 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 "basmethnode.hxx" +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/frame/DispatchHelper.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <vcl/svapp.hxx> +#include <basic/sbstar.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbmod.hxx> + +#include <util/MiscUtils.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::comphelper; +using namespace ::com::sun::star::script; +using namespace ::sf_misc; + +#define BASPROV_PROPERTY_ID_URI 1 +#define BASPROV_PROPERTY_ID_EDITABLE 2 + +constexpr OUStringLiteral BASPROV_PROPERTY_URI = u"URI"; +constexpr OUStringLiteral BASPROV_PROPERTY_EDITABLE = u"Editable"; + +#define BASPROV_DEFAULT_ATTRIBS() PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY + + +namespace basprov +{ + + + // BasicMethodNodeImpl + + + BasicMethodNodeImpl::BasicMethodNodeImpl( const Reference< XComponentContext >& rxContext, + const OUString& sScriptingContext, SbMethod* pMethod, bool isAppScript ) + : ::scripting_helper::OBroadcastHelperHolder( m_aMutex ) + ,OPropertyContainer( GetBroadcastHelper() ) + ,m_xContext( rxContext ) + ,m_sScriptingContext( sScriptingContext ) + ,m_pMethod( pMethod ) + ,m_bIsAppScript( isAppScript ) + ,m_bEditable( true ) + { + if ( m_pMethod ) + { + SbModule* pModule = m_pMethod->GetModule(); + if ( pModule ) + { + StarBASIC* pBasic = static_cast< StarBASIC* >( pModule->GetParent() ); + if ( pBasic ) + { + m_sURI = "vnd.sun.star.script:"; + m_sURI += pBasic->GetName(); + m_sURI += "."; + m_sURI += pModule->GetName(); + m_sURI += "."; + m_sURI += m_pMethod->GetName(); + m_sURI += "?language=Basic&location="; + if ( m_bIsAppScript ) + m_sURI += "application"; + else + m_sURI += "document"; + } + } + } + + registerProperty( BASPROV_PROPERTY_URI, BASPROV_PROPERTY_ID_URI, BASPROV_DEFAULT_ATTRIBS(), &m_sURI, cppu::UnoType<decltype(m_sURI)>::get() ); + registerProperty( BASPROV_PROPERTY_EDITABLE, BASPROV_PROPERTY_ID_EDITABLE, BASPROV_DEFAULT_ATTRIBS(), &m_bEditable, cppu::UnoType<decltype(m_bEditable)>::get() ); + } + + + BasicMethodNodeImpl::~BasicMethodNodeImpl() + { + } + + + // XInterface + + + IMPLEMENT_FORWARD_XINTERFACE2( BasicMethodNodeImpl, BasicMethodNodeImpl_BASE, OPropertyContainer ) + + + // XTypeProvider + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( BasicMethodNodeImpl, BasicMethodNodeImpl_BASE, OPropertyContainer ) + + + // XBrowseNode + + + OUString BasicMethodNodeImpl::getName( ) + { + SolarMutexGuard aGuard; + + OUString sMethodName; + if ( m_pMethod ) + sMethodName = m_pMethod->GetName(); + + return sMethodName; + } + + + Sequence< Reference< browse::XBrowseNode > > BasicMethodNodeImpl::getChildNodes( ) + { + return Sequence< Reference< browse::XBrowseNode > >(); + } + + + sal_Bool BasicMethodNodeImpl::hasChildNodes( ) + { + return false; + } + + + sal_Int16 BasicMethodNodeImpl::getType( ) + { + return browse::BrowseNodeTypes::SCRIPT; + } + + + // OPropertySetHelper + + + ::cppu::IPropertyArrayHelper& BasicMethodNodeImpl::getInfoHelper( ) + { + return *getArrayHelper(); + } + + + // OPropertyArrayUsageHelper + + + ::cppu::IPropertyArrayHelper* BasicMethodNodeImpl::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + + // XPropertySet + + + Reference< XPropertySetInfo > BasicMethodNodeImpl::getPropertySetInfo( ) + { + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + // XInvocation + + + Reference< XIntrospectionAccess > BasicMethodNodeImpl::getIntrospection( ) + { + return Reference< XIntrospectionAccess >(); + } + + + Any BasicMethodNodeImpl::invoke( const OUString& aFunctionName, const Sequence< Any >&, + Sequence< sal_Int16 >&, Sequence< Any >& ) + { + if ( aFunctionName != BASPROV_PROPERTY_EDITABLE ) + { + throw IllegalArgumentException( + "BasicMethodNodeImpl::invoke: function name not supported!", + Reference< XInterface >(), 1 ); + } + + OUString sDocURL, sLibName, sModName; + sal_uInt16 nLine1 = 0; + + if ( !m_bIsAppScript ) + { + Reference< frame::XModel > xModel = MiscUtils::tDocUrlToModel( m_sScriptingContext ); + + if ( xModel.is() ) + { + sDocURL = xModel->getURL(); + if ( sDocURL.isEmpty() ) + { + const Sequence < PropertyValue > aProps = xModel->getArgs(); + // TODO: according to MBA the property 'Title' may change in future + const PropertyValue* pProp = std::find_if(aProps.begin(), aProps.end(), + [](const PropertyValue& rProp) { return rProp.Name == "Title"; }); + if (pProp != aProps.end()) + pProp->Value >>= sDocURL; + } + } + } + + if ( m_pMethod ) + { + sal_uInt16 nLine2; + m_pMethod->GetLineRange( nLine1, nLine2 ); + SbModule* pModule = m_pMethod->GetModule(); + if ( pModule ) + { + sModName = pModule->GetName(); + StarBASIC* pBasic = static_cast< StarBASIC* >( pModule->GetParent() ); + if ( pBasic ) + sLibName = pBasic->GetName(); + } + } + + if ( m_xContext.is() ) + { + Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( m_xContext ); + + Reference < frame::XDispatchProvider > xProv( xDesktop->getCurrentFrame(), UNO_QUERY ); + + if ( xProv.is() ) + { + Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create( m_xContext ) ); + + Sequence < PropertyValue > aArgs{ + comphelper::makePropertyValue("Document", sDocURL), + comphelper::makePropertyValue("LibName", sLibName), + comphelper::makePropertyValue("Name", sModName), + comphelper::makePropertyValue("Type", OUString("Module")), + comphelper::makePropertyValue("Line", static_cast< sal_uInt32 >( nLine1 )) + }; + xHelper->executeDispatch( xProv, ".uno:BasicIDEAppear", OUString(), 0, aArgs ); + } + } + + + return Any(); + } + + + void BasicMethodNodeImpl::setValue( const OUString&, const Any& ) + { + throw UnknownPropertyException( + "BasicMethodNodeImpl::setValue: property name is unknown!" ); + } + + + Any BasicMethodNodeImpl::getValue( const OUString& ) + { + throw UnknownPropertyException( + "BasicMethodNodeImpl::getValue: property name is unknown!" ); + } + + + sal_Bool BasicMethodNodeImpl::hasMethod( const OUString& aName ) + { + bool bReturn = false; + if ( aName == BASPROV_PROPERTY_EDITABLE ) + bReturn = true; + + return bReturn; + } + + + sal_Bool BasicMethodNodeImpl::hasProperty( const OUString& ) + { + return false; + } + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basmethnode.hxx b/scripting/source/basprov/basmethnode.hxx new file mode 100644 index 000000000..d1eddada0 --- /dev/null +++ b/scripting/source/basprov/basmethnode.hxx @@ -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 . + */ + +#pragma once + +#include <bcholder.hxx> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/script/browse/XBrowseNode.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <comphelper/uno3.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/implbase.hxx> + + +class SbMethod; + + +namespace basprov +{ + + + + + typedef ::cppu::WeakImplHelper< + css::script::browse::XBrowseNode, + css::script::XInvocation > BasicMethodNodeImpl_BASE; + + class BasicMethodNodeImpl : public BasicMethodNodeImpl_BASE, + public cppu::BaseMutex, + public ::scripting_helper::OBroadcastHelperHolder, + public ::comphelper::OPropertyContainer, + public ::comphelper::OPropertyArrayUsageHelper< BasicMethodNodeImpl > + { + private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + OUString m_sScriptingContext; + SbMethod* m_pMethod; + bool m_bIsAppScript; + + // properties + OUString m_sURI; + bool m_bEditable; + + protected: + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + public: + BasicMethodNodeImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sScriptingContext, + SbMethod* pMethod, bool isAppScript ); + virtual ~BasicMethodNodeImpl() override; + + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XBrowseNode + virtual OUString SAL_CALL getName( ) override; + virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override; + virtual sal_Bool SAL_CALL hasChildNodes( ) override; + virtual sal_Int16 SAL_CALL getType( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XInvocation + virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection( ) override; + virtual css::uno::Any SAL_CALL invoke( + const OUString& aFunctionName, + const css::uno::Sequence< css::uno::Any >& aParams, + css::uno::Sequence< sal_Int16 >& aOutParamIndex, + css::uno::Sequence< css::uno::Any >& aOutParam ) override; + virtual void SAL_CALL setValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getValue( const OUString& aPropertyName ) override; + virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override; + }; + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basmodnode.cxx b/scripting/source/basprov/basmodnode.cxx new file mode 100644 index 000000000..986e3062d --- /dev/null +++ b/scripting/source/basprov/basmodnode.cxx @@ -0,0 +1,134 @@ +/* -*- 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 "basmodnode.hxx" +#include "basmethnode.hxx" +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> +#include <vcl/svapp.hxx> +#include <basic/sbx.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbmeth.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; + + +namespace basprov +{ + + + // BasicModuleNodeImpl + + + BasicModuleNodeImpl::BasicModuleNodeImpl( const Reference< XComponentContext >& rxContext, + const OUString& sScriptingContext, SbModule* pModule, bool isAppScript ) + :m_xContext( rxContext ) + ,m_sScriptingContext( sScriptingContext ) + ,m_pModule( pModule ) + ,m_bIsAppScript( isAppScript ) + { + } + + + BasicModuleNodeImpl::~BasicModuleNodeImpl() + { + } + + + // XBrowseNode + + + OUString BasicModuleNodeImpl::getName( ) + { + SolarMutexGuard aGuard; + + OUString sModuleName; + if ( m_pModule ) + sModuleName = m_pModule->GetName(); + + return sModuleName; + } + + + Sequence< Reference< browse::XBrowseNode > > BasicModuleNodeImpl::getChildNodes( ) + { + SolarMutexGuard aGuard; + + Sequence< Reference< browse::XBrowseNode > > aChildNodes; + + if ( m_pModule ) + { + SbxArray* pMethods = m_pModule->GetMethods().get(); + if ( pMethods ) + { + sal_uInt32 nCount = pMethods->Count(); + sal_Int32 nRealCount = 0; + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Get(i)); + if ( pMethod && !pMethod->IsHidden() ) + ++nRealCount; + } + aChildNodes.realloc( nRealCount ); + Reference< browse::XBrowseNode >* pChildNodes = aChildNodes.getArray(); + + sal_Int32 iTarget = 0; + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Get(i)); + if ( pMethod && !pMethod->IsHidden() ) + pChildNodes[iTarget++] = new BasicMethodNodeImpl( + m_xContext, m_sScriptingContext, pMethod, m_bIsAppScript); + } + } + } + + return aChildNodes; + } + + + sal_Bool BasicModuleNodeImpl::hasChildNodes( ) + { + SolarMutexGuard aGuard; + + bool bReturn = false; + if ( m_pModule ) + { + SbxArray* pMethods = m_pModule->GetMethods().get(); + if (pMethods && pMethods->Count() > 0) + bReturn = true; + } + + return bReturn; + } + + + sal_Int16 BasicModuleNodeImpl::getType( ) + { + return browse::BrowseNodeTypes::CONTAINER; + } + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basmodnode.hxx b/scripting/source/basprov/basmodnode.hxx new file mode 100644 index 000000000..ebf0957ef --- /dev/null +++ b/scripting/source/basprov/basmodnode.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/script/browse/XBrowseNode.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +class SbModule; + + +namespace basprov +{ + + + + + typedef ::cppu::WeakImplHelper< + css::script::browse::XBrowseNode > BasicModuleNodeImpl_BASE; + + + class BasicModuleNodeImpl : public BasicModuleNodeImpl_BASE + { + private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + OUString m_sScriptingContext; + SbModule* m_pModule; + bool m_bIsAppScript; + + public: + BasicModuleNodeImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& sScriptingContext, + SbModule* pModule, bool isAppScript ); + virtual ~BasicModuleNodeImpl() override; + + // XBrowseNode + virtual OUString SAL_CALL getName( ) override; + virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override; + virtual sal_Bool SAL_CALL hasChildNodes( ) override; + virtual sal_Int16 SAL_CALL getType( ) override; + }; + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basprov.component b/scripting/source/basprov/basprov.component new file mode 100644 index 000000000..d0d6bf974 --- /dev/null +++ b/scripting/source/basprov/basprov.component @@ -0,0 +1,29 @@ +<?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.scripting.ScriptProviderForBasic" + constructor="scripting_BasicProviderImpl_get_implementation"> + <service name="com.sun.star.script.browse.BrowseNode"/> + <service name="com.sun.star.script.provider.LanguageScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProviderForBasic"/> + </implementation> +</component> diff --git a/scripting/source/basprov/basprov.cxx b/scripting/source/basprov/basprov.cxx new file mode 100644 index 000000000..d4afb41ea --- /dev/null +++ b/scripting/source/basprov/basprov.cxx @@ -0,0 +1,477 @@ +/* -*- 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 "basprov.hxx" +#include "basscript.hxx" +#include "baslibnode.hxx" +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp> +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> + +#include <cppuhelper/supportsservice.hxx> +#include <rtl/uri.hxx> +#include <sal/log.hxx> +#include <osl/file.hxx> +#include <vcl/svapp.hxx> +#include <basic/basmgr.hxx> +#include <basic/basicmanagerrepository.hxx> +#include <basic/sbstar.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbmeth.hxx> +#include <sfx2/app.hxx> + +#include <com/sun/star/util/theMacroExpander.hpp> +#include <com/sun/star/script/XLibraryContainer2.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> + +#include <util/MiscUtils.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::document; +using namespace ::sf_misc; + + +namespace basprov +{ + + // BasicProviderImpl + + + BasicProviderImpl::BasicProviderImpl( const Reference< XComponentContext >& xContext ) + :m_pAppBasicManager( nullptr ) + ,m_pDocBasicManager( nullptr ) + ,m_xContext( xContext ) + ,m_bIsAppScriptCtx( true ) + ,m_bIsUserCtx(true) + { + } + + + BasicProviderImpl::~BasicProviderImpl() + { + SolarMutexGuard aGuard; + EndListeningAll(); + } + + + bool BasicProviderImpl::isLibraryShared( const Reference< script::XLibraryContainer >& rxLibContainer, const OUString& rLibName ) + { + bool bIsShared = false; + + Reference< script::XLibraryContainer2 > xLibContainer( rxLibContainer, UNO_QUERY ); + if ( xLibContainer.is() && xLibContainer->hasByName( rLibName ) && xLibContainer->isLibraryLink( rLibName ) ) + { + OUString aFileURL; + if ( m_xContext.is() ) + { + Reference< uri::XUriReferenceFactory > xUriFac( uri::UriReferenceFactory::create( m_xContext ) ); + + OUString aLinkURL( xLibContainer->getLibraryLinkURL( rLibName ) ); + Reference< uri::XUriReference > xUriRef = xUriFac->parse( aLinkURL ); + + if ( xUriRef.is() ) + { + OUString aScheme = xUriRef->getScheme(); + if ( aScheme.equalsIgnoreAsciiCase("file") ) + { + aFileURL = aLinkURL; + } + else if ( aScheme.equalsIgnoreAsciiCase("vnd.sun.star.pkg") ) + { + OUString aAuthority = xUriRef->getAuthority(); + if ( aAuthority.matchIgnoreAsciiCase( "vnd.sun.star.expand:" ) ) + { + OUString aDecodedURL( aAuthority.copy( sizeof ( "vnd.sun.star.expand:" ) - 1 ) ); + aDecodedURL = ::rtl::Uri::decode( aDecodedURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 ); + Reference<util::XMacroExpander> xMacroExpander = + util::theMacroExpander::get(m_xContext); + aFileURL = xMacroExpander->expandMacros( aDecodedURL ); + } + } + } + } + + if ( !aFileURL.isEmpty() ) + { + osl::DirectoryItem aFileItem; + osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL ); + OSL_VERIFY( osl::DirectoryItem::get( aFileURL, aFileItem ) == osl::FileBase::E_None ); + OSL_VERIFY( aFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None ); + OUString aCanonicalFileURL( aFileStatus.getFileURL() ); + + if( aCanonicalFileURL.indexOf( "share/basic" ) != -1 + || aCanonicalFileURL.indexOf( "share/uno_packages" ) != -1 ) + bIsShared = true; + } + } + + return bIsShared; + } + + // SfxListener + void BasicProviderImpl::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) + { + if (auto pManager = dynamic_cast<const BasicManager*>(&rBC)) + if (pManager == m_pAppBasicManager && rHint.GetId() == SfxHintId::Dying) + { + EndListening(*m_pAppBasicManager); + m_pAppBasicManager = nullptr; + } + } + + // XServiceInfo + OUString BasicProviderImpl::getImplementationName( ) + { + return "com.sun.star.comp.scripting.ScriptProviderForBasic"; + } + + sal_Bool BasicProviderImpl::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + Sequence< OUString > BasicProviderImpl::getSupportedServiceNames( ) + { + return { + "com.sun.star.script.provider.ScriptProviderForBasic", + "com.sun.star.script.provider.LanguageScriptProvider", + "com.sun.star.script.provider.ScriptProvider", + "com.sun.star.script.browse.BrowseNode"}; + } + + + // XInitialization + + + void BasicProviderImpl::initialize( const Sequence< Any >& aArguments ) + { + // TODO + + SolarMutexGuard aGuard; + + if ( aArguments.getLength() != 1 ) + { + throw IllegalArgumentException( + "BasicProviderImpl::initialize: incorrect argument count.", + *this, + 1 + ); + } + + Reference< frame::XModel > xModel; + + m_xInvocationContext.set( aArguments[0], UNO_QUERY ); + if ( m_xInvocationContext.is() ) + { + xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY ); + if ( !xModel.is() ) + { + throw IllegalArgumentException( + "BasicProviderImpl::initialize: unable to determine the document model from the script invocation context.", + *this, + 1 + ); + } + } + else + { + if ( !( aArguments[0] >>= m_sScriptingContext ) ) + { + throw IllegalArgumentException( + "BasicProviderImpl::initialize: incorrect argument type " + aArguments[0].getValueTypeName(), + *this, + 1 + ); + } + + if ( m_sScriptingContext.startsWith( "vnd.sun.star.tdoc" ) ) + { + xModel = MiscUtils::tDocUrlToModel( m_sScriptingContext ); + } + } + + if ( xModel.is() ) + { + Reference< XEmbeddedScripts > xDocumentScripts( xModel, UNO_QUERY ); + if ( xDocumentScripts.is() ) + { + m_pDocBasicManager = ::basic::BasicManagerRepository::getDocumentBasicManager( xModel ); + m_xLibContainerDoc = xDocumentScripts->getBasicLibraries(); + OSL_ENSURE( m_pDocBasicManager && m_xLibContainerDoc.is(), + "BasicProviderImpl::initialize: invalid BasicManager, or invalid script container!" ); + } + m_bIsAppScriptCtx = false; + } + else + { + // Provider has been created with application context for user + // or share + if ( m_sScriptingContext != "user" ) + { + m_bIsUserCtx = false; + } + else + { + /* + throw RuntimeException( + "BasicProviderImpl::initialize: no scripting context!" ); + */ + } + } + + // TODO + if ( !m_pAppBasicManager ) + { + m_pAppBasicManager = SfxApplication::GetBasicManager(); + if (m_pAppBasicManager) + StartListening(*m_pAppBasicManager); + } + + if ( !m_xLibContainerApp.is() ) + m_xLibContainerApp = SfxGetpApp()->GetBasicContainer(); + } + + + // XScriptProvider + + + Reference < provider::XScript > BasicProviderImpl::getScript( const OUString& scriptURI ) + { + // TODO + + SolarMutexGuard aGuard; + + Reference< provider::XScript > xScript; + Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) ); + + Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI ); + + Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY ); + + if ( !uriRef.is() || !sfUri.is() ) + { + throw provider::ScriptFrameworkErrorException( + "BasicProviderImpl::getScript: failed to parse URI: " + scriptURI, + Reference< XInterface >(), + scriptURI, "Basic", + provider::ScriptFrameworkErrorType::MALFORMED_URL ); + } + + + OUString aDescription = sfUri->getName(); + OUString aLocation = sfUri->getParameter( "location" ); + + sal_Int32 nIndex = 0; + // In some strange circumstances the Library name can have an + // apparently illegal '.' in it ( in imported VBA ) + + BasicManager* pBasicMgr = nullptr; + if ( aLocation == "document" ) + { + pBasicMgr = m_pDocBasicManager; + } + else if ( aLocation == "application" ) + { + pBasicMgr = m_pAppBasicManager; + } + OUString sProjectName; + if ( pBasicMgr ) + sProjectName = pBasicMgr->GetName(); + + OUString aLibrary; + if ( !sProjectName.isEmpty() && aDescription.match( sProjectName ) ) + { + SAL_WARN("scripting", "LibraryName " << sProjectName << " is part of the url " << aDescription ); + aLibrary = sProjectName; + nIndex = sProjectName.getLength() + 1; + } + else + aLibrary = aDescription.getToken( 0, '.', nIndex ); + OUString aModule; + if ( nIndex != -1 ) + aModule = aDescription.getToken( 0, '.', nIndex ); + OUString aMethod; + if ( nIndex != -1 ) + aMethod = aDescription.getToken( 0, '.', nIndex ); + + if ( !aLibrary.isEmpty() && !aModule.isEmpty() && !aMethod.isEmpty() && !aLocation.isEmpty() ) + { + + if ( pBasicMgr ) + { + StarBASIC* pBasic = pBasicMgr->GetLib( aLibrary ); + if ( !pBasic ) + { + sal_uInt16 nId = pBasicMgr->GetLibId( aLibrary ); + if ( nId != LIB_NOTFOUND ) + { + pBasicMgr->LoadLib( nId ); + pBasic = pBasicMgr->GetLib( aLibrary ); + } + } + if ( pBasic ) + { + SbModule* pModule = pBasic->FindModule( aModule ); + if ( pModule ) + { + SbMethod* pMethod = pModule->FindMethod( aMethod, SbxClassType::Method ); + if ( pMethod && !pMethod->IsHidden() ) + { + if ( m_pDocBasicManager == pBasicMgr ) + xScript = new BasicScriptImpl( aDescription, pMethod, *m_pDocBasicManager, m_xInvocationContext ); + else + xScript = new BasicScriptImpl( aDescription, pMethod ); + } + } + } + } + } + + if ( !xScript.is() ) + { + throw provider::ScriptFrameworkErrorException( + "The following Basic script could not be found:\n" + "library: '" + aLibrary + "'\n" + "module: '" + aModule + "'\n" + "method: '" + aMethod + "'\n" + "location: '" + aLocation + "'\n", + Reference< XInterface >(), + scriptURI, "Basic", + provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT ); + } + + return xScript; + } + + + // XBrowseNode + + + OUString BasicProviderImpl::getName( ) + { + return "Basic"; + } + + + Sequence< Reference< browse::XBrowseNode > > BasicProviderImpl::getChildNodes( ) + { + SolarMutexGuard aGuard; + + Reference< script::XLibraryContainer > xLibContainer; + BasicManager* pBasicManager = nullptr; + + if ( m_bIsAppScriptCtx ) + { + xLibContainer = m_xLibContainerApp; + pBasicManager = m_pAppBasicManager; + } + else + { + xLibContainer = m_xLibContainerDoc; + pBasicManager = m_pDocBasicManager; + } + + Sequence< Reference< browse::XBrowseNode > > aChildNodes; + + if ( pBasicManager && xLibContainer.is() ) + { + const Sequence< OUString > aLibNames = xLibContainer->getElementNames(); + sal_Int32 nLibCount = aLibNames.getLength(); + aChildNodes.realloc( nLibCount ); + Reference< browse::XBrowseNode >* pChildNodes = aChildNodes.getArray(); + sal_Int32 childrenFound = 0; + + for ( const OUString& rLibName : aLibNames ) + { + bool bCreate = false; + if ( m_bIsAppScriptCtx ) + { + const bool bShared = isLibraryShared( xLibContainer, rLibName ); + if (m_bIsUserCtx != bShared) + bCreate = true; + } + else + { + bCreate = true; + } + if ( bCreate ) + { + pChildNodes[childrenFound++] + = new BasicLibraryNodeImpl(m_xContext, m_sScriptingContext, pBasicManager, + xLibContainer, rLibName, m_bIsAppScriptCtx); + } + } + + if ( childrenFound != nLibCount ) + aChildNodes.realloc( childrenFound ); + } + + return aChildNodes; + } + + + sal_Bool BasicProviderImpl::hasChildNodes( ) + { + SolarMutexGuard aGuard; + + bool bReturn = false; + Reference< script::XLibraryContainer > xLibContainer; + if ( m_bIsAppScriptCtx ) + { + xLibContainer = m_xLibContainerApp; + } + else + { + xLibContainer = m_xLibContainerDoc; + } + if ( xLibContainer.is() ) + bReturn = xLibContainer->hasElements(); + + return bReturn; + } + + + sal_Int16 BasicProviderImpl::getType( ) + { + return browse::BrowseNodeTypes::CONTAINER; + } + + + // component operations + + extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* + scripting_BasicProviderImpl_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) + { + return cppu::acquire(new BasicProviderImpl(context)); + } + + +} // namespace basprov + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basprov.hxx b/scripting/source/basprov/basprov.hxx new file mode 100644 index 000000000..994113b62 --- /dev/null +++ b/scripting/source/basprov/basprov.hxx @@ -0,0 +1,97 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/browse/XBrowseNode.hpp> +#include <com/sun/star/script/provider/XScriptProvider.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> +#include <svl/lstner.hxx> + +class BasicManager; + + +namespace basprov +{ + + + + + typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::script::provider::XScriptProvider, + css::script::browse::XBrowseNode > BasicProviderImpl_BASE; + + + class BasicProviderImpl : public BasicProviderImpl_BASE, public SfxListener + { + private: + BasicManager* m_pAppBasicManager; + BasicManager* m_pDocBasicManager; + css::uno::Reference< css::script::XLibraryContainer > m_xLibContainerApp; + css::uno::Reference< css::script::XLibraryContainer > m_xLibContainerDoc; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::document::XScriptInvocationContext > m_xInvocationContext; + OUString m_sScriptingContext; + bool m_bIsAppScriptCtx; + bool m_bIsUserCtx; + + bool isLibraryShared( + const css::uno::Reference< css::script::XLibraryContainer >& rxLibContainer, + const OUString& rLibName ); + + public: + explicit BasicProviderImpl( + const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~BasicProviderImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XScriptProvider + virtual css::uno::Reference < css::script::provider::XScript > SAL_CALL getScript( + const OUString& scriptURI ) override; + + // XBrowseNode + virtual OUString SAL_CALL getName( ) override; + virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override; + virtual sal_Bool SAL_CALL hasChildNodes( ) override; + virtual sal_Int16 SAL_CALL getType( ) override; + + protected: + // SfxListener + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + }; + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basscript.cxx b/scripting/source/basprov/basscript.cxx new file mode 100644 index 000000000..b5a3f6136 --- /dev/null +++ b/scripting/source/basprov/basscript.cxx @@ -0,0 +1,314 @@ +/* -*- 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 "basscript.hxx" +#include <vcl/svapp.hxx> +#include <basic/sbx.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbuno.hxx> +#include <basic/basmgr.hxx> +#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp> +#include <bcholder.hxx> +#include <comphelper/propertycontainer.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <map> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::beans; + + +namespace basprov +{ + +#define BASSCRIPT_PROPERTY_ID_CALLER 1 +constexpr OUStringLiteral BASSCRIPT_PROPERTY_CALLER = u"Caller"; + +#define BASSCRIPT_DEFAULT_ATTRIBS() PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT + + typedef ::std::map< sal_Int16, Any > OutParamMap; + + + // BasicScriptImpl + + + BasicScriptImpl::BasicScriptImpl( const OUString& funcName, SbMethodRef const & xMethod ) + : ::scripting_helper::OBroadcastHelperHolder( m_aMutex ) + ,OPropertyContainer( GetBroadcastHelper() ) + ,m_xMethod( xMethod ) + ,m_funcName( funcName ) + ,m_documentBasicManager( nullptr ) + ,m_xDocumentScriptContext() + { + registerProperty( BASSCRIPT_PROPERTY_CALLER, BASSCRIPT_PROPERTY_ID_CALLER, BASSCRIPT_DEFAULT_ATTRIBS(), &m_caller, cppu::UnoType<decltype(m_caller)>::get() ); + } + + + BasicScriptImpl::BasicScriptImpl( const OUString& funcName, SbMethodRef const & xMethod, + BasicManager& documentBasicManager, const Reference< XScriptInvocationContext >& documentScriptContext ) : ::scripting_helper::OBroadcastHelperHolder( m_aMutex ) + ,OPropertyContainer( GetBroadcastHelper() ) + ,m_xMethod( xMethod ) + ,m_funcName( funcName ) + ,m_documentBasicManager( &documentBasicManager ) + ,m_xDocumentScriptContext( documentScriptContext ) + { + StartListening( *m_documentBasicManager ); + registerProperty( BASSCRIPT_PROPERTY_CALLER, BASSCRIPT_PROPERTY_ID_CALLER, BASSCRIPT_DEFAULT_ATTRIBS(), &m_caller, cppu::UnoType<decltype(m_caller)>::get() ); + } + + + BasicScriptImpl::~BasicScriptImpl() + { + SolarMutexGuard g; + + if ( m_documentBasicManager ) + EndListening( *m_documentBasicManager ); + } + + + // SfxListener + + void BasicScriptImpl::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) + { + if ( &rBC != m_documentBasicManager ) + { + OSL_ENSURE( false, "BasicScriptImpl::Notify: where does this come from?" ); + // not interested in + return; + } + if ( rHint.GetId() == SfxHintId::Dying ) + { + m_documentBasicManager = nullptr; + EndListening( rBC ); // prevent multiple notifications + } + } + + + // XInterface + + + IMPLEMENT_FORWARD_XINTERFACE2( BasicScriptImpl, BasicScriptImpl_BASE, OPropertyContainer ) + + + // XTypeProvider + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( BasicScriptImpl, BasicScriptImpl_BASE, OPropertyContainer ) + + + // OPropertySetHelper + + + ::cppu::IPropertyArrayHelper& BasicScriptImpl::getInfoHelper( ) + { + return *getArrayHelper(); + } + + + // OPropertyArrayUsageHelper + + + ::cppu::IPropertyArrayHelper* BasicScriptImpl::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + + // XPropertySet + + + Reference< XPropertySetInfo > BasicScriptImpl::getPropertySetInfo( ) + { + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + + // XScript + + + Any BasicScriptImpl::invoke( const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) + { + // TODO: throw CannotConvertException + // TODO: check length of aOutParamIndex, aOutParam + + SolarMutexGuard aGuard; + + Any aReturn; + + if ( m_xMethod.is() ) + { + // check if compiled + SbModule* pModule = static_cast< SbModule* >( m_xMethod->GetParent() ); + if ( pModule && !pModule->IsCompiled() ) + pModule->Compile(); + + // check number of parameters + sal_Int32 nParamsCount = aParams.getLength(); + SbxInfo* pInfo = m_xMethod->GetInfo(); + if ( pInfo ) + { + sal_Int32 nSbxOptional = 0; + sal_uInt16 n = 1; + for ( const SbxParamInfo* pParamInfo = pInfo->GetParam( n ); pParamInfo; pParamInfo = pInfo->GetParam( ++n ) ) + { + if ( pParamInfo->nFlags & SbxFlagBits::Optional ) + ++nSbxOptional; + else + nSbxOptional = 0; + } + sal_Int32 nSbxCount = n - 1; + if ( nParamsCount < nSbxCount - nSbxOptional ) + { + throw provider::ScriptFrameworkErrorException( + "wrong number of parameters!", + Reference< XInterface >(), + m_funcName, + "Basic", + provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT ); + } + } + + // set parameters + SbxArrayRef xSbxParams; + if ( nParamsCount > 0 ) + { + xSbxParams = new SbxArray; + const Any* pParams = aParams.getConstArray(); + for ( sal_Int32 i = 0; i < nParamsCount; ++i ) + { + SbxVariableRef xSbxVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xSbxVar.get(), pParams[i] ); + xSbxParams->Put(xSbxVar.get(), static_cast<sal_uInt32>(i) + 1); + + if (pInfo) + { + if (auto* p = pInfo->GetParam(static_cast<sal_uInt16>(i) + 1)) + { + SbxDataType t = static_cast<SbxDataType>(p->eType & 0x0FFF); + // tdf#133889 Revert the downcasting performed in sbxToUnoValueImpl + // to allow passing by reference. + SbxDataType a = xSbxVar->GetType(); + if (t == SbxSINGLE && (a == SbxINTEGER || a == SbxLONG)) + { + sal_Int32 val = xSbxVar->GetLong(); + if (val >= -16777216 && val <= 16777215) + xSbxVar->SetType(t); + } + else if (t == SbxDOUBLE && (a == SbxINTEGER || a == SbxLONG)) + xSbxVar->SetType(t); + else if (t == SbxLONG && a == SbxINTEGER) + xSbxVar->SetType(t); + else if (t == SbxULONG && a == SbxUSHORT) + xSbxVar->SetType(t); + // Enable passing by ref + if (t != SbxVARIANT) + xSbxVar->SetFlag(SbxFlagBits::Fixed); + } + } + } + } + if ( xSbxParams.is() ) + m_xMethod->SetParameters( xSbxParams.get() ); + + // call method + SbxVariableRef xReturn = new SbxVariable; + ErrCode nErr = ERRCODE_NONE; + + // if it's a document-based script, temporarily reset ThisComponent to the script invocation context + Any aOldThisComponent; + if ( m_documentBasicManager && m_xDocumentScriptContext.is() ) + m_documentBasicManager->SetGlobalUNOConstant( "ThisComponent", Any( m_xDocumentScriptContext ), &aOldThisComponent ); + + if ( m_caller.hasElements() && m_caller[ 0 ].hasValue() ) + { + SbxVariableRef xCallerVar = new SbxVariable( SbxVARIANT ); + unoToSbxValue( xCallerVar.get(), m_caller[ 0 ] ); + nErr = m_xMethod->Call( xReturn.get(), xCallerVar.get() ); + } + else + nErr = m_xMethod->Call( xReturn.get() ); + + if ( m_documentBasicManager && m_xDocumentScriptContext.is() ) + m_documentBasicManager->SetGlobalUNOConstant( "ThisComponent", aOldThisComponent ); + + if ( nErr != ERRCODE_NONE ) + { + // TODO: throw InvocationTargetException ? + } + + // get output parameters + if ( xSbxParams.is() ) + { + SbxInfo* pInfo_ = m_xMethod->GetInfo(); + if ( pInfo_ ) + { + OutParamMap aOutParamMap; + for (sal_uInt32 n = 1, nCount = xSbxParams->Count(); n < nCount; ++n) + { + assert(nCount <= std::numeric_limits<sal_uInt16>::max()); + const SbxParamInfo* pParamInfo = pInfo_->GetParam( sal::static_int_cast<sal_uInt16>(n) ); + if ( pParamInfo && ( pParamInfo->eType & SbxBYREF ) != 0 ) + { + SbxVariable* pVar = xSbxParams->Get(n); + if ( pVar ) + { + SbxVariableRef xVar = pVar; + aOutParamMap.emplace( n - 1, sbxToUnoValue( xVar.get() ) ); + } + } + } + sal_Int32 nOutParamCount = aOutParamMap.size(); + aOutParamIndex.realloc( nOutParamCount ); + aOutParam.realloc( nOutParamCount ); + sal_Int16* pOutParamIndex = aOutParamIndex.getArray(); + Any* pOutParam = aOutParam.getArray(); + for ( const auto& rEntry : aOutParamMap ) + { + *pOutParamIndex = rEntry.first; + ++pOutParamIndex; + *pOutParam = rEntry.second; + ++pOutParam; + } + } + } + + // get return value + aReturn = sbxToUnoValue( xReturn.get() ); + + // reset parameters + m_xMethod->SetParameters( nullptr ); + } + + return aReturn; + } + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/basprov/basscript.hxx b/scripting/source/basprov/basscript.hxx new file mode 100644 index 000000000..d0f9e6e7a --- /dev/null +++ b/scripting/source/basprov/basscript.hxx @@ -0,0 +1,103 @@ +/* -*- 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 . + */ + +#pragma once + +#include <bcholder.hxx> +#include <com/sun/star/script/provider/XScript.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <basic/sbmeth.hxx> +#include <svl/lstner.hxx> + +class BasicManager; + + +namespace basprov +{ + + + + + typedef ::cppu::WeakImplHelper< + css::script::provider::XScript > BasicScriptImpl_BASE; + + + class BasicScriptImpl : public BasicScriptImpl_BASE, public SfxListener, + public cppu::BaseMutex, + public ::scripting_helper::OBroadcastHelperHolder, + public ::comphelper::OPropertyContainer, + public ::comphelper::OPropertyArrayUsageHelper< BasicScriptImpl > + { + private: + SbMethodRef m_xMethod; + OUString m_funcName; + BasicManager* m_documentBasicManager; + css::uno::Reference< css::document::XScriptInvocationContext > + m_xDocumentScriptContext; + // hack, OPropertyContainer doesn't allow you to define a property of unknown + // type ( I guess because an Any can't contain an Any... I've always wondered why? + // as it's not unusual to do that in corba ) + css::uno::Sequence< css::uno::Any > m_caller; + protected: + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + public: + BasicScriptImpl( + const OUString& funcName, + SbMethodRef const & xMethod + ); + BasicScriptImpl( + const OUString& funcName, + SbMethodRef const & xMethod, + BasicManager& documentBasicManager, + const css::uno::Reference< css::document::XScriptInvocationContext >& documentScriptContext + ); + virtual ~BasicScriptImpl() override; + + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + + // XScript + virtual css::uno::Any SAL_CALL invoke( + const css::uno::Sequence< css::uno::Any >& aParams, + css::uno::Sequence< sal_Int16 >& aOutParamIndex, + css::uno::Sequence< css::uno::Any >& aOutParam ) override; + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + }; + + +} // namespace basprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/dlgprov/DialogModelProvider.cxx b/scripting/source/dlgprov/DialogModelProvider.cxx new file mode 100644 index 000000000..e49ed058d --- /dev/null +++ b/scripting/source/dlgprov/DialogModelProvider.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 "DialogModelProvider.hxx" +#include "dlgprov.hxx" +#include <com/sun/star/resource/XStringResourceManager.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <cppuhelper/supportsservice.hxx> + +/// anonymous implementation namespace +namespace dlgprov { + +using namespace ::com::sun::star; +using namespace awt; +using namespace lang; +using namespace uno; +using namespace script; +using namespace beans; + + +DialogModelProvider::DialogModelProvider(Reference< XComponentContext > const & context) : + m_xContext(context) +{} + +// lang::XInitialization: +void SAL_CALL DialogModelProvider::initialize(const css::uno::Sequence< uno::Any > & aArguments) +{ + if ( aArguments.getLength() != 1 ) + return; + + OUString sURL; + if ( !( aArguments[ 0 ] >>= sURL )) + throw css::lang::IllegalArgumentException(); + // Try any other URL with SimpleFileAccess + Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create(m_xContext); + + try + { + Reference< io::XInputStream > xInput = xSFI->openFileRead( sURL ); + Reference< resource::XStringResourceManager > xStringResourceManager; + if ( xInput.is() ) + { + xStringResourceManager = dlgprov::lcl_getStringResourceManager(m_xContext,sURL); + Any aDialogSourceURLAny; + aDialogSourceURLAny <<= sURL; + + Reference< frame::XModel > xModel; + m_xDialogModel.set( dlgprov::lcl_createDialogModel( m_xContext, xInput , xModel, xStringResourceManager, aDialogSourceURLAny ), UNO_SET_THROW); + m_xDialogModelProp.set(m_xDialogModel, UNO_QUERY_THROW); + } + } + catch( Exception& ) + {} + //m_sURL = sURL; +} + +// container::XElementAccess: +uno::Type SAL_CALL DialogModelProvider::getElementType() +{ + return m_xDialogModel->getElementType(); +} + +sal_Bool SAL_CALL DialogModelProvider::hasElements() +{ + return m_xDialogModel->hasElements(); +} + +// container::XNameAccess: +uno::Any SAL_CALL DialogModelProvider::getByName(const OUString & aName) +{ + return m_xDialogModel->getByName(aName); +} + +css::uno::Sequence< OUString > SAL_CALL DialogModelProvider::getElementNames() +{ + return m_xDialogModel->getElementNames(); +} + +sal_Bool SAL_CALL DialogModelProvider::hasByName(const OUString & aName) +{ + return m_xDialogModel->hasByName(aName); +} + +// container::XNameReplace: +void SAL_CALL DialogModelProvider::replaceByName(const OUString & aName, const uno::Any & aElement) +{ + m_xDialogModel->replaceByName(aName,aElement); +} + +// container::XNameContainer: +void SAL_CALL DialogModelProvider::insertByName(const OUString & aName, const uno::Any & aElement) +{ + m_xDialogModel->insertByName(aName,aElement); +} + +void SAL_CALL DialogModelProvider::removeByName(const OUString & aName) +{ + m_xDialogModel->removeByName(aName); +} +uno::Reference< beans::XPropertySetInfo > SAL_CALL DialogModelProvider::getPropertySetInfo( ) +{ + return m_xDialogModelProp->getPropertySetInfo(); +} +void SAL_CALL DialogModelProvider::setPropertyValue( const OUString&, const uno::Any& ) +{ +} +uno::Any SAL_CALL DialogModelProvider::getPropertyValue( const OUString& PropertyName ) +{ + return m_xDialogModelProp->getPropertyValue(PropertyName); +} +void SAL_CALL DialogModelProvider::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) +{ +} +void SAL_CALL DialogModelProvider::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) +{ +} +void SAL_CALL DialogModelProvider::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) +{ +} +void SAL_CALL DialogModelProvider::removeVetoableChangeListener( const OUString& ,const uno::Reference< beans::XVetoableChangeListener >& ) +{ +} + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL DialogModelProvider::getImplementationName() +{ + return "com.sun.star.comp.scripting.DialogModelProvider"; +} + +sal_Bool SAL_CALL DialogModelProvider::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > SAL_CALL DialogModelProvider::getSupportedServiceNames() +{ + return { "com.sun.star.awt.UnoControlDialogModelProvider" }; +} + +} // closing anonymous implementation namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_DialogModelProvider_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new dlgprov::DialogModelProvider(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/dlgprov/DialogModelProvider.hxx b/scripting/source/dlgprov/DialogModelProvider.hxx new file mode 100644 index 000000000..d9a41ef64 --- /dev/null +++ b/scripting/source/dlgprov/DialogModelProvider.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +/// anonymous implementation namespace +namespace dlgprov{ + +class DialogModelProvider: + public ::cppu::WeakImplHelper< + css::lang::XInitialization, + css::container::XNameContainer, + css::beans::XPropertySet, + css::lang::XServiceInfo> +{ +public: + explicit DialogModelProvider(css::uno::Reference< css::uno::XComponentContext > const & context); +private: + // css::lang::XInitialization: + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > & aArguments) override; + + // css::container::XElementAccess: + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // css::container::XNameAccess: + virtual css::uno::Any SAL_CALL getByName(const OUString & aName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName(const OUString & aName) override; + + // css::container::XNameReplace: + virtual void SAL_CALL replaceByName(const OUString & aName, const css::uno::Any & aElement) override; + + // css::container::XNameContainer: + virtual void SAL_CALL insertByName(const OUString & aName, const css::uno::Any & aElement) override; + virtual void SAL_CALL removeByName(const OUString & Name) override; + + // css::lang::XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + +private: + DialogModelProvider(const DialogModelProvider &) = delete; + DialogModelProvider& operator=(const DialogModelProvider &) = delete; + + // destructor is private and will be called indirectly by the release call virtual ~DialogModelProvider() {} + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::container::XNameContainer> m_xDialogModel; + css::uno::Reference< css::beans::XPropertySet> m_xDialogModelProp; +}; +} // closing anonymous implementation namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/dlgprov/dlgevtatt.cxx b/scripting/source/dlgprov/dlgevtatt.cxx new file mode 100644 index 000000000..704518e07 --- /dev/null +++ b/scripting/source/dlgprov/dlgevtatt.cxx @@ -0,0 +1,658 @@ +/* -*- 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 "dlgevtatt.hxx" + +#include "dlgprov.hxx" + +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <tools/diagnose_ex.h> + +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/XDialogEventHandler.hpp> +#include <com/sun/star/awt/XContainerWindowEventHandler.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/script/XScriptEventsSupplier.hpp> +#include <com/sun/star/script/provider/XScriptProvider.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/reflection/XIdlMethod.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> + +#include <ooo/vba/XVBAToOOEventDescGen.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::reflection; + + +namespace dlgprov +{ + namespace { + + class DialogSFScriptListenerImpl : public DialogScriptListenerImpl + { + protected: + Reference< frame::XModel > m_xModel; + virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override; + public: + DialogSFScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< frame::XModel >& rxModel ) : DialogScriptListenerImpl( rxContext ), m_xModel( rxModel ) {} + }; + + class DialogLegacyScriptListenerImpl : public DialogSFScriptListenerImpl + { + protected: + virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override; + public: + DialogLegacyScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< frame::XModel >& rxModel ) : DialogSFScriptListenerImpl( rxContext, rxModel ){} + }; + + class DialogUnoScriptListenerImpl : public DialogSFScriptListenerImpl + { + Reference< awt::XControl > m_xControl; + Reference< XInterface > m_xHandler; + Reference< beans::XIntrospectionAccess > m_xIntrospectionAccess; + bool m_bDialogProviderMode; + + virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override; + + public: + DialogUnoScriptListenerImpl( const Reference< XComponentContext >& rxContext, + const Reference< frame::XModel >& rxModel, + const Reference< awt::XControl >& rxControl, + const Reference< XInterface >& rxHandler, + const Reference< beans::XIntrospectionAccess >& rxIntrospectionAccess, + bool bDialogProviderMode ); // false: ContainerWindowProvider mode + + }; + + class DialogVBAScriptListenerImpl : public DialogScriptListenerImpl + { + protected: + OUString msDialogCodeName; + OUString msDialogLibName; + Reference< script::XScriptListener > mxListener; + virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override; + public: + DialogVBAScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< awt::XControl >& rxControl, const Reference< frame::XModel >& xModel, const OUString& sDialogLibName ); + }; + + } + + DialogVBAScriptListenerImpl::DialogVBAScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< awt::XControl >& rxControl, const Reference< frame::XModel >& xModel, const OUString& sDialogLibName ) : DialogScriptListenerImpl( rxContext ), msDialogLibName( sDialogLibName ) + { + Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager() ); + Sequence< Any > args(1); + if ( xSMgr.is() ) + { + args.getArray()[0] <<= xModel; + mxListener.set( xSMgr->createInstanceWithArgumentsAndContext( "ooo.vba.EventListener", args, m_xContext ), UNO_QUERY ); + } + if ( !rxControl.is() ) + return; + + try + { + Reference< XPropertySet > xProps( rxControl->getModel(), UNO_QUERY_THROW ); + xProps->getPropertyValue("Name") >>= msDialogCodeName; + xProps.set( mxListener, UNO_QUERY_THROW ); + xProps->setPropertyValue("Model", args[ 0 ] ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + + } + + void DialogVBAScriptListenerImpl::firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* ) + { + if ( !(aScriptEvent.ScriptType == "VBAInterop" && mxListener.is()) ) + return; + + ScriptEvent aScriptEventCopy( aScriptEvent ); + aScriptEventCopy.ScriptCode = msDialogLibName + "." + msDialogCodeName; + try + { + mxListener->firing( aScriptEventCopy ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + } + + + // DialogEventsAttacherImpl + + + DialogEventsAttacherImpl::DialogEventsAttacherImpl( const Reference< XComponentContext >& rxContext, const Reference< frame::XModel >& rxModel, const Reference< awt::XControl >& rxControl, const Reference< XInterface >& rxHandler, const Reference< beans::XIntrospectionAccess >& rxIntrospect, bool bProviderMode, const Reference< script::XScriptListener >& rxRTLListener, const OUString& sDialogLibName ) + :mbUseFakeVBAEvents( false ), m_xContext( rxContext ) + { + // key listeners by protocol when ScriptType = 'Script' + // otherwise key is the ScriptType e.g. StarBasic + if ( rxRTLListener.is() ) // set up handler for RTL_BASIC + listenersForTypes[ OUString("StarBasic") ] = rxRTLListener; + else + listenersForTypes[ OUString("StarBasic") ] = new DialogLegacyScriptListenerImpl( rxContext, rxModel ); + // handler for Script & OUString("vnd.sun.star.UNO:") + listenersForTypes[ OUString("vnd.sun.star.UNO") ] = new DialogUnoScriptListenerImpl( rxContext, rxModel, rxControl, rxHandler, rxIntrospect, bProviderMode ); + listenersForTypes[ OUString("vnd.sun.star.script") ] = new DialogSFScriptListenerImpl( rxContext, rxModel ); + + // determine the VBA compatibility mode from the Basic library container + try + { + uno::Reference< beans::XPropertySet > xModelProps( rxModel, uno::UNO_QUERY_THROW ); + uno::Reference< script::vba::XVBACompatibility > xVBACompat( + xModelProps->getPropertyValue("BasicLibraries"), uno::UNO_QUERY_THROW ); + mbUseFakeVBAEvents = xVBACompat->getVBACompatibilityMode(); + } + catch( uno::Exception& ) + { + } + if ( mbUseFakeVBAEvents ) + listenersForTypes[ OUString("VBAInterop") ] = new DialogVBAScriptListenerImpl( rxContext, rxControl, rxModel, sDialogLibName ); + } + + + DialogEventsAttacherImpl::~DialogEventsAttacherImpl() + { + } + + + Reference< script::XScriptListener > const & + DialogEventsAttacherImpl::getScriptListenerForKey( const OUString& sKey ) + { + ListenerHash::iterator it = listenersForTypes.find( sKey ); + if ( it == listenersForTypes.end() ) + throw RuntimeException(); // more text info here please + return it->second; + } + Reference< XScriptEventsSupplier > DialogEventsAttacherImpl::getFakeVbaEventsSupplier( const Reference< XControl >& xControl, OUString const & sControlName ) + { + Reference< XScriptEventsSupplier > xEventsSupplier; + Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager() ); + if ( xSMgr.is() ) + { + Reference< ooo::vba::XVBAToOOEventDescGen > xVBAToOOEvtDesc( xSMgr->createInstanceWithContext("ooo.vba.VBAToOOEventDesc", m_xContext ), UNO_QUERY ); + if ( xVBAToOOEvtDesc.is() ) + xEventsSupplier = xVBAToOOEvtDesc->getEventSupplier( xControl, sControlName ); + + } + return xEventsSupplier; + } + + + void DialogEventsAttacherImpl::attachEventsToControl( const Reference< XControl>& xControl, const Reference< XScriptEventsSupplier >& xEventsSupplier, const Any& Helper ) + { + if ( !xEventsSupplier.is() ) + return; + + Reference< container::XNameContainer > xEventCont = xEventsSupplier->getEvents(); + + Reference< XControlModel > xControlModel = xControl->getModel(); + if ( !xEventCont.is() ) + return; + + const Sequence< OUString > aNames = xEventCont->getElementNames(); + + for ( const OUString& rName : aNames ) + { + ScriptEventDescriptor aDesc; + + Any aElement = xEventCont->getByName( rName ); + aElement >>= aDesc; + OUString sKey = aDesc.ScriptType; + if ( aDesc.ScriptType == "Script" || aDesc.ScriptType == "UNO" ) + { + sal_Int32 nIndex = aDesc.ScriptCode.indexOf( ':' ); + sKey = aDesc.ScriptCode.copy( 0, nIndex ); + } + Reference< XAllListener > xAllListener = + new DialogAllListenerImpl( getScriptListenerForKey( sKey ), aDesc.ScriptType, aDesc.ScriptCode ); + + // try first to attach event to the ControlModel + bool bSuccess = false; + try + { + Reference< XEventListener > xListener_ = m_xEventAttacher->attachSingleEventListener( + xControlModel, xAllListener, Helper, aDesc.ListenerType, + aDesc.AddListenerParam, aDesc.EventMethod ); + + if ( xListener_.is() ) + bSuccess = true; + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + + try + { + // if we had no success, try to attach to the control + if ( !bSuccess ) + { + m_xEventAttacher->attachSingleEventListener( + xControl, xAllListener, Helper, aDesc.ListenerType, + aDesc.AddListenerParam, aDesc.EventMethod ); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + } + } + + + void DialogEventsAttacherImpl::nestedAttachEvents( const Sequence< Reference< XInterface > >& Objects, const Any& Helper, OUString& sDialogCodeName ) + { + for ( const Reference< XInterface >& rObject : Objects ) + { + // We know that we have to do with instances of XControl. + // Otherwise this is not the right implementation for + // XScriptEventsAttacher and we have to give up. + Reference< XControl > xControl( rObject, UNO_QUERY ); + Reference< XControlContainer > xControlContainer( xControl, UNO_QUERY ); + Reference< XDialog > xDialog( xControl, UNO_QUERY ); + if ( !xControl.is() ) + throw IllegalArgumentException(); + + // get XEventsSupplier from control model + Reference< XControlModel > xControlModel = xControl->getModel(); + Reference< XScriptEventsSupplier > xEventsSupplier( xControlModel, UNO_QUERY ); + attachEventsToControl( xControl, xEventsSupplier, Helper ); + if ( mbUseFakeVBAEvents ) + { + xEventsSupplier.set( getFakeVbaEventsSupplier( xControl, sDialogCodeName ) ); + Any newHelper(xControl ); + attachEventsToControl( xControl, xEventsSupplier, newHelper ); + } + if ( xControlContainer.is() && !xDialog.is() ) + { + Sequence< Reference< XControl > > aControls = xControlContainer->getControls(); + sal_Int32 nControlCount = aControls.getLength(); + + Sequence< Reference< XInterface > > aObjects( nControlCount ); + Reference< XInterface >* pObjects2 = aObjects.getArray(); + const Reference< XControl >* pControls = aControls.getConstArray(); + + for ( sal_Int32 i2 = 0; i2 < nControlCount; ++i2 ) + { + pObjects2[i2].set( pControls[i2], UNO_QUERY ); + } + nestedAttachEvents( aObjects, Helper, sDialogCodeName ); + } + } + } + + + // XScriptEventsAttacher + + + void SAL_CALL DialogEventsAttacherImpl::attachEvents( const Sequence< Reference< XInterface > >& Objects, + const css::uno::Reference<css::script::XScriptListener>&, + const Any& Helper ) + { + // get EventAttacher + { + ::osl::MutexGuard aGuard( getMutex() ); + + if ( !m_xEventAttacher.is() ) + { + Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager() ); + if ( !xSMgr.is() ) + throw RuntimeException(); + + m_xEventAttacher.set( xSMgr->createInstanceWithContext( + "com.sun.star.script.EventAttacher", m_xContext ), UNO_QUERY ); + + if ( !m_xEventAttacher.is() ) + throw ServiceNotRegisteredException(); + } + } + OUString sDialogCodeName; + sal_Int32 nObjCount = Objects.getLength(); + Reference< awt::XControl > xDlgControl( Objects[ nObjCount - 1 ], uno::UNO_QUERY ); // last object is the dialog + if ( xDlgControl.is() ) + { + Reference< XPropertySet > xProps( xDlgControl->getModel(), UNO_QUERY ); + try + { + xProps->getPropertyValue("Name") >>= sDialogCodeName; + } + catch( Exception& ){} + } + // go over all objects + nestedAttachEvents( Objects, Helper, sDialogCodeName ); + } + + + // DialogAllListenerImpl + + + DialogAllListenerImpl::DialogAllListenerImpl( const Reference< XScriptListener >& rxListener, + const OUString& rScriptType, const OUString& rScriptCode ) + :m_xScriptListener( rxListener ) + ,m_sScriptType( rScriptType ) + ,m_sScriptCode( rScriptCode ) + { + } + + + DialogAllListenerImpl::~DialogAllListenerImpl() + { + } + + + void DialogAllListenerImpl::firing_impl( const AllEventObject& Event, Any* pRet ) + { + ScriptEvent aScriptEvent; + aScriptEvent.Source = static_cast<OWeakObject *>(this); // get correct XInterface + aScriptEvent.ListenerType = Event.ListenerType; + aScriptEvent.MethodName = Event.MethodName; + aScriptEvent.Arguments = Event.Arguments; + aScriptEvent.Helper = Event.Helper; + aScriptEvent.ScriptType = m_sScriptType; + aScriptEvent.ScriptCode = m_sScriptCode; + + if ( m_xScriptListener.is() ) + { + if ( pRet ) + *pRet = m_xScriptListener->approveFiring( aScriptEvent ); + else + m_xScriptListener->firing( aScriptEvent ); + } + } + + + // XEventListener + + + void DialogAllListenerImpl::disposing(const EventObject& ) + { + } + + + // XAllListener + + + void DialogAllListenerImpl::firing( const AllEventObject& Event ) + { + //::osl::MutexGuard aGuard( getMutex() ); + + firing_impl( Event, nullptr ); + } + + + Any DialogAllListenerImpl::approveFiring( const AllEventObject& Event ) + { + //::osl::MutexGuard aGuard( getMutex() ); + + Any aReturn; + firing_impl( Event, &aReturn ); + return aReturn; + } + + + // DialogScriptListenerImpl + + + DialogUnoScriptListenerImpl::DialogUnoScriptListenerImpl( const Reference< XComponentContext >& rxContext, + const Reference< css::frame::XModel >& rxModel, + const Reference< css::awt::XControl >& rxControl, + const Reference< css::uno::XInterface >& rxHandler, + const Reference< css::beans::XIntrospectionAccess >& rxIntrospectionAccess, + bool bDialogProviderMode ) + : DialogSFScriptListenerImpl( rxContext, rxModel ) + ,m_xControl( rxControl ) + ,m_xHandler( rxHandler ) + ,m_xIntrospectionAccess( rxIntrospectionAccess ) + ,m_bDialogProviderMode( bDialogProviderMode ) + { + } + + + DialogScriptListenerImpl::~DialogScriptListenerImpl() + { + } + + + void DialogSFScriptListenerImpl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet ) + { + try + { + Reference< provider::XScriptProvider > xScriptProvider; + if ( m_xModel.is() ) + { + Reference< provider::XScriptProviderSupplier > xSupplier( m_xModel, UNO_QUERY ); + OSL_ENSURE( xSupplier.is(), "DialogScriptListenerImpl::firing_impl: failed to get script provider supplier" ); + if ( xSupplier.is() ) + xScriptProvider.set( xSupplier->getScriptProvider() ); + } + else + { + OSL_ASSERT( m_xContext.is() ); + if ( m_xContext.is() ) + { + Reference< provider::XScriptProviderFactory > xFactory = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + Any aCtx; + aCtx <<= OUString("user"); + xScriptProvider = xFactory->createScriptProvider( aCtx ); + } + } + + OSL_ENSURE( xScriptProvider.is(), "DialogScriptListenerImpl::firing_impl: failed to get script provider" ); + + if ( xScriptProvider.is() ) + { + Reference< provider::XScript > xScript = xScriptProvider->getScript( aScriptEvent.ScriptCode ); + OSL_ENSURE( xScript.is(), "DialogScriptListenerImpl::firing_impl: failed to get script" ); + + if ( xScript.is() ) + { + Sequence< Any > aInParams; + Sequence< sal_Int16 > aOutParamsIndex; + Sequence< Any > aOutParams; + + // get arguments for script + aInParams = aScriptEvent.Arguments; + + Any aResult = xScript->invoke( aInParams, aOutParamsIndex, aOutParams ); + if ( pRet ) + *pRet = aResult; + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + } + + void DialogLegacyScriptListenerImpl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet ) + { + OUString sScriptURL; + OUString sScriptCode( aScriptEvent.ScriptCode ); + + if ( aScriptEvent.ScriptType != "StarBasic" ) + return; + + // StarBasic script: convert ScriptCode to scriptURL + sal_Int32 nIndex = sScriptCode.indexOf( ':' ); + if ( nIndex >= 0 && nIndex < sScriptCode.getLength() ) + { + sScriptURL = OUString::Concat("vnd.sun.star.script:") + + sScriptCode.subView( nIndex + 1 ) + + "?language=Basic&location=" + + sScriptCode.subView( 0, nIndex ); + } + ScriptEvent aSFScriptEvent( aScriptEvent ); + aSFScriptEvent.ScriptCode = sScriptURL; + DialogSFScriptListenerImpl::firing_impl( aSFScriptEvent, pRet ); + } + + void DialogUnoScriptListenerImpl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet ) + { + OUString aMethodName = aScriptEvent.ScriptCode.copy( strlen("vnd.sun.star.UNO:") ); + + const Any* pArguments = aScriptEvent.Arguments.getConstArray(); + Any aEventObject = pArguments[0]; + + bool bHandled = false; + if( m_xHandler.is() ) + { + if( m_bDialogProviderMode ) + { + Reference< XDialogEventHandler > xDialogEventHandler( m_xHandler, UNO_QUERY ); + if( xDialogEventHandler.is() ) + { + Reference< XDialog > xDialog( m_xControl, UNO_QUERY ); + bHandled = xDialogEventHandler->callHandlerMethod( xDialog, aEventObject, aMethodName ); + } + } + else + { + Reference< XContainerWindowEventHandler > xContainerWindowEventHandler( m_xHandler, UNO_QUERY ); + if( xContainerWindowEventHandler.is() ) + { + Reference< XWindow > xWindow( m_xControl, UNO_QUERY ); + bHandled = xContainerWindowEventHandler->callHandlerMethod( xWindow, aEventObject, aMethodName ); + } + } + } + + Any aRet; + if( !bHandled && m_xIntrospectionAccess.is() ) + { + try + { + // call method + const Reference< XIdlMethod >& rxMethod = m_xIntrospectionAccess-> + getMethod( aMethodName, MethodConcept::ALL - MethodConcept::DANGEROUS ); + + Reference< XMaterialHolder > xMaterialHolder = + Reference< XMaterialHolder >::query( m_xIntrospectionAccess ); + Any aHandlerObject = xMaterialHolder->getMaterial(); + + Sequence< Reference< XIdlClass > > aParamTypeSeq = rxMethod->getParameterTypes(); + sal_Int32 nParamCount = aParamTypeSeq.getLength(); + if( nParamCount == 0 ) + { + Sequence<Any> args; + rxMethod->invoke( aHandlerObject, args ); + bHandled = true; + } + else if( nParamCount == 2 ) + { + // Signature check automatically done by reflection + Sequence<Any> Args(2); + Any* pArgs = Args.getArray(); + if( m_bDialogProviderMode ) + { + Reference< XDialog > xDialog( m_xControl, UNO_QUERY ); + pArgs[0] <<= xDialog; + } + else + { + Reference< XWindow > xWindow( m_xControl, UNO_QUERY ); + pArgs[0] <<= xWindow; + } + pArgs[1] = aEventObject; + aRet = rxMethod->invoke( aHandlerObject, Args ); + bHandled = true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + } + + if( bHandled ) + { + if( pRet ) + *pRet = aRet; + } + else + { + OUString aRes(SfxResId(STR_ERRUNOEVENTBINDUNG)); + OUString aQuoteChar( "\"" ); + + sal_Int32 nIndex = aRes.indexOf( '%' ); + + OUString aOUFinal = + aRes.subView( 0, nIndex ) + + aQuoteChar + aMethodName + aQuoteChar + + aRes.subView( nIndex + 2 ); + + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, aOUFinal)); + xBox->run(); + } + } + + + // XEventListener + + + void DialogScriptListenerImpl::disposing(const EventObject& ) + { + } + + + // XScriptListener + + + void DialogScriptListenerImpl::firing( const ScriptEvent& aScriptEvent ) + { + //::osl::MutexGuard aGuard( getMutex() ); + + firing_impl( aScriptEvent, nullptr ); + } + + + Any DialogScriptListenerImpl::approveFiring( const ScriptEvent& aScriptEvent ) + { + //::osl::MutexGuard aGuard( getMutex() ); + + Any aReturn; + firing_impl( aScriptEvent, &aReturn ); + return aReturn; + } + + +} // namespace dlgprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/dlgprov/dlgevtatt.hxx b/scripting/source/dlgprov/dlgevtatt.hxx new file mode 100644 index 000000000..239cb6530 --- /dev/null +++ b/scripting/source/dlgprov/dlgevtatt.hxx @@ -0,0 +1,130 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/script/XAllListener.hpp> +#include <com/sun/star/script/XEventAttacher.hpp> +#include <com/sun/star/script/XScriptEventsAttacher.hpp> +#include <com/sun/star/script/XScriptListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/script/XScriptEventsSupplier.hpp> + +#include <unordered_map> + +namespace dlgprov +{ + typedef std::unordered_map< OUString, + css::uno::Reference< css::script::XScriptListener > > ListenerHash; + + typedef ::cppu::WeakImplHelper< + css::script::XScriptEventsAttacher > DialogEventsAttacherImpl_BASE; + + + class DialogEventsAttacherImpl : public DialogEventsAttacherImpl_BASE + { + private: + bool mbUseFakeVBAEvents; + ListenerHash listenersForTypes; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::script::XEventAttacher > m_xEventAttacher; + /// @throws css::uno::RuntimeException + css::uno::Reference< css::script::XScriptListener > const & getScriptListenerForKey( const OUString& sScriptName ); + css::uno::Reference< css::script::XScriptEventsSupplier > getFakeVbaEventsSupplier( const css::uno::Reference< css::awt::XControl>& xControl, OUString const & sCodeName ); + void nestedAttachEvents( const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& Objects, const css::uno::Any& Helper, OUString& sDialogCodeName ); + void attachEventsToControl( const css::uno::Reference< css::awt::XControl>& xControl, const css::uno::Reference< css::script::XScriptEventsSupplier >& events, const css::uno::Any& Helper ); + public: + DialogEventsAttacherImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::awt::XControl >& xControl, + const css::uno::Reference< css::uno::XInterface >& xHandler, + const css::uno::Reference< css::beans::XIntrospectionAccess >& xIntrospect, + bool bProviderMode, + const css::uno::Reference< css::script::XScriptListener >& xRTLListener ,const OUString& sDialogLibName ); + virtual ~DialogEventsAttacherImpl() override; + + // XScriptEventsAttacher + virtual void SAL_CALL attachEvents( const css::uno::Sequence< + css::uno::Reference< css::uno::XInterface > >& Objects, + const css::uno::Reference<css::script::XScriptListener>&, + const css::uno::Any& Helper ) override; + }; + + + + + typedef ::cppu::WeakImplHelper< + css::script::XAllListener > DialogAllListenerImpl_BASE; + + + class DialogAllListenerImpl : public DialogAllListenerImpl_BASE + { + private: + css::uno::Reference< css::script::XScriptListener > m_xScriptListener; + OUString m_sScriptType; + OUString m_sScriptCode; + + void firing_impl( const css::script::AllEventObject& Event, css::uno::Any* pRet ); + + public: + DialogAllListenerImpl( const css::uno::Reference< css::script::XScriptListener >& rxListener, + const OUString& rScriptType, const OUString& rScriptCode ); + virtual ~DialogAllListenerImpl() override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XAllListener + virtual void SAL_CALL firing( const css::script::AllEventObject& Event ) override; + virtual css::uno::Any SAL_CALL approveFiring( const css::script::AllEventObject& Event ) override; + }; + + + + + typedef ::cppu::WeakImplHelper< + css::script::XScriptListener > DialogScriptListenerImpl_BASE; + + + class DialogScriptListenerImpl : public DialogScriptListenerImpl_BASE + { + protected: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + virtual void firing_impl( const css::script::ScriptEvent& aScriptEvent, css::uno::Any* pRet ) = 0; + public: + explicit DialogScriptListenerImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) : m_xContext( rxContext ) {} + virtual ~DialogScriptListenerImpl() override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XScriptListener + virtual void SAL_CALL firing( const css::script::ScriptEvent& aScriptEvent ) override; + virtual css::uno::Any SAL_CALL approveFiring( const css::script::ScriptEvent& aScriptEvent ) override; + }; + + +} // namespace dlgprov + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/dlgprov/dlgprov.component b/scripting/source/dlgprov/dlgprov.component new file mode 100644 index 000000000..35203afb8 --- /dev/null +++ b/scripting/source/dlgprov/dlgprov.component @@ -0,0 +1,32 @@ +<?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.scripting.DialogProvider" + constructor="scripting_DialogProviderImpl_get_implementation"> + <service name="com.sun.star.awt.ContainerWindowProvider"/> + <service name="com.sun.star.awt.DialogProvider"/> + <service name="com.sun.star.awt.DialogProvider2"/> + </implementation> + <implementation name="com.sun.star.comp.scripting.DialogModelProvider" + constructor="scripting_DialogModelProvider_get_implementation"> + <service name="com.sun.star.awt.UnoControlDialogModelProvider"/> + </implementation> +</component> diff --git a/scripting/source/dlgprov/dlgprov.cxx b/scripting/source/dlgprov/dlgprov.cxx new file mode 100644 index 000000000..18815f499 --- /dev/null +++ b/scripting/source/dlgprov/dlgprov.cxx @@ -0,0 +1,702 @@ +/* -*- 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 "dlgprov.hxx" +#include "dlgevtatt.hxx" +#include <com/sun/star/awt/UnoControlDialog.hpp> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/io/XInputStreamProvider.hpp> +#include <com/sun/star/resource/XStringResourceSupplier.hpp> +#include <com/sun/star/resource/XStringResourceManager.hpp> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrl.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sfx2/app.hxx> +#include <xmlscript/xmldlg_imexp.hxx> +#include <tools/urlobj.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <util/MiscUtils.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <i18nlangtag/languagetag.hxx> + +using namespace ::com::sun::star; +using namespace awt; +using namespace lang; +using namespace uno; +using namespace script; +using namespace beans; +using namespace document; +using namespace ::sf_misc; + +namespace dlgprov +{ + + Reference< resource::XStringResourceManager > lcl_getStringResourceManager(const Reference< XComponentContext >& i_xContext, std::u16string_view i_sURL) + { + INetURLObject aInetObj( i_sURL ); + OUString aDlgName = aInetObj.GetBase(); + aInetObj.removeSegment(); + OUString aDlgLocation = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + css::lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale(); + + Reference< task::XInteractionHandler > xDummyHandler; + + Sequence<Any> aArgs{ Any(aDlgLocation), + Any(true), // bReadOnly + Any(aLocale), + Any(aDlgName), + Any(OUString()), + Any(xDummyHandler) }; + + Reference< XMultiComponentFactory > xSMgr_( i_xContext->getServiceManager(), UNO_SET_THROW ); + // TODO: Ctor + Reference< resource::XStringResourceManager > xStringResourceManager( xSMgr_->createInstanceWithContext + ( "com.sun.star.resource.StringResourceWithLocation", + i_xContext ), UNO_QUERY ); + if( xStringResourceManager.is() ) + { + Reference< XInitialization > xInit( xStringResourceManager, UNO_QUERY ); + if( xInit.is() ) + xInit->initialize( aArgs ); + } + return xStringResourceManager; + } + Reference< container::XNameContainer > lcl_createControlModel(const Reference< XComponentContext >& i_xContext) + { + Reference< XMultiComponentFactory > xSMgr_( i_xContext->getServiceManager(), UNO_SET_THROW ); + Reference< container::XNameContainer > xControlModel( xSMgr_->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", i_xContext ), UNO_QUERY_THROW ); + return xControlModel; + } + Reference< container::XNameContainer > lcl_createDialogModel( const Reference< XComponentContext >& i_xContext, + const Reference< io::XInputStream >& xInput, + const Reference< frame::XModel >& xModel, + const Reference< resource::XStringResourceManager >& xStringResourceManager, + const Any &aDialogSourceURL) + { + Reference< container::XNameContainer > xDialogModel( lcl_createControlModel(i_xContext) ); + + Reference< beans::XPropertySet > xDlgPropSet( xDialogModel, UNO_QUERY ); + xDlgPropSet->setPropertyValue( "DialogSourceURL", aDialogSourceURL ); + + // #TODO we really need to detect the source of the Dialog, is it + // the dialog. E.g. if the dialog was created from basic ( then we just + // can't tell where its from ) + // If we are happy to always substitute the form model for the awt + // one then maybe the presence of a document model is enough to trigger + // swapping out the models ( or perhaps we only want to do this + // for vba mode ) there are a number of feasible and valid possibilities + ::xmlscript::importDialogModel( xInput, xDialogModel, i_xContext, xModel ); + + // Set resource property + if( xStringResourceManager.is() ) + { + Reference< beans::XPropertySet > xDlgPSet( xDialogModel, UNO_QUERY ); + Any aStringResourceManagerAny; + aStringResourceManagerAny <<= xStringResourceManager; + xDlgPSet->setPropertyValue( "ResourceResolver", aStringResourceManagerAny ); + } + + return xDialogModel; + } + + // mutex + + + ::osl::Mutex& getMutex() + { + static ::osl::Mutex s_aMutex; + + return s_aMutex; + } + + + // DialogProviderImpl + + + DialogProviderImpl::DialogProviderImpl( const Reference< XComponentContext >& rxContext ) + :m_xContext( rxContext ) + { + } + + + DialogProviderImpl::~DialogProviderImpl() + { + } + + + static Reference< resource::XStringResourceManager > getStringResourceFromDialogLibrary + ( const Reference< container::XNameContainer >& xDialogLib ) + { + Reference< resource::XStringResourceManager > xStringResourceManager; + if( xDialogLib.is() ) + { + Reference< resource::XStringResourceSupplier > xStringResourceSupplier( xDialogLib, UNO_QUERY ); + if( xStringResourceSupplier.is() ) + { + Reference< resource::XStringResourceResolver > + xStringResourceResolver = xStringResourceSupplier->getStringResource(); + + xStringResourceManager = + Reference< resource::XStringResourceManager >( xStringResourceResolver, UNO_QUERY ); + } + } + return xStringResourceManager; + } + + Reference< container::XNameContainer > DialogProviderImpl::createDialogModel( + const Reference< io::XInputStream >& xInput, + const Reference< resource::XStringResourceManager >& xStringResourceManager, + const Any &aDialogSourceURL) + { + return lcl_createDialogModel(m_xContext,xInput,m_xModel,xStringResourceManager,aDialogSourceURL); + } + + Reference< XControlModel > DialogProviderImpl::createDialogModelForBasic() + { + if (!m_BasicInfo) + // shouldn't get here + throw RuntimeException("No information to create dialog" ); + Reference< resource::XStringResourceManager > xStringResourceManager = getStringResourceFromDialogLibrary( m_BasicInfo->mxDlgLib ); + + Any aDialogSourceURL((OUString())); + Reference< XControlModel > xCtrlModel( createDialogModel( m_BasicInfo->mxInput, xStringResourceManager, aDialogSourceURL ), UNO_QUERY_THROW ); + return xCtrlModel; + } + + Reference< XControlModel > DialogProviderImpl::createDialogModel( const OUString& sURL ) + { + + OUString aURL( sURL ); + + // parse URL + // TODO: use URL parsing class + // TODO: decoding of location + + Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) ); + + // i75778: Support non-script URLs + Reference< io::XInputStream > xInput; + Reference< container::XNameContainer > xDialogLib; + + // Accept file URL to single dialog + bool bSingleDialog = false; + + Reference< util::XMacroExpander > xMacroExpander = + util::theMacroExpander::get(m_xContext); + + Reference< uri::XUriReference > uriRef; + for (;;) + { + uriRef = xFac->parse( aURL ); + if ( !uriRef.is() ) + { + OUString errorMsg = "DialogProviderImpl::getDialogModel: failed to parse URI: " + aURL; + throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 1 ); + } + Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY ); + if( !sxUri.is() ) + break; + + aURL = sxUri->expand( xMacroExpander ); + } + + Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY ); + if( !sfUri.is() ) + { + bSingleDialog = true; + + // Try any other URL with SimpleFileAccess + Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create(m_xContext); + + try + { + xInput = xSFI->openFileRead( aURL ); + } + catch( Exception& ) + {} + } + else + { + OUString sDescription = sfUri->getName(); + + sal_Int32 nIndex = 0; + + OUString sLibName = sDescription.getToken( 0, '.', nIndex ); + OUString sDlgName; + if ( nIndex != -1 ) + sDlgName = sDescription.getToken( 0, '.', nIndex ); + + OUString sLocation = sfUri->getParameter( "location" ); + + + // get dialog library container + // TODO: dialogs in packages + Reference< XLibraryContainer > xLibContainer; + + if ( sLocation == "application" ) + { + xLibContainer = SfxGetpApp()->GetDialogContainer(); + } + else if ( sLocation == "document" ) + { + Reference< XEmbeddedScripts > xDocumentScripts( m_xModel, UNO_QUERY ); + if ( xDocumentScripts.is() ) + { + xLibContainer = xDocumentScripts->getDialogLibraries(); + OSL_ENSURE( xLibContainer.is(), + "DialogProviderImpl::createDialogModel: invalid dialog container!" ); + } + } + else + { + const Sequence< OUString > aOpenDocsTdocURLs( MiscUtils::allOpenTDocUrls( m_xContext ) ); + for ( auto const & tdocURL : aOpenDocsTdocURLs ) + { + Reference< frame::XModel > xModel( MiscUtils::tDocUrlToModel( tdocURL ) ); + OSL_ENSURE( xModel.is(), "DialogProviderImpl::createDialogModel: invalid document model!" ); + if ( !xModel.is() ) + continue; + + OUString sDocURL = xModel->getURL(); + if ( sDocURL.isEmpty() ) + { + sDocURL = ::comphelper::NamedValueCollection::getOrDefault( xModel->getArgs(), u"Title", sDocURL ); + } + + if ( sLocation != sDocURL ) + continue; + + Reference< XEmbeddedScripts > xDocumentScripts( m_xModel, UNO_QUERY ); + if ( !xDocumentScripts.is() ) + continue; + + xLibContainer = xDocumentScripts->getDialogLibraries(); + OSL_ENSURE( xLibContainer.is(), + "DialogProviderImpl::createDialogModel: invalid dialog container!" ); + } + } + + // get input stream provider + Reference< io::XInputStreamProvider > xISP; + if ( !xLibContainer.is() ) + { + throw IllegalArgumentException( + "DialogProviderImpl::getDialog: library container not found!", + Reference< XInterface >(), 1 ); + } + + // load dialog library + if ( !xLibContainer->isLibraryLoaded( sLibName ) ) + xLibContainer->loadLibrary( sLibName ); + + // get dialog library + if ( xLibContainer->hasByName( sLibName ) ) + { + Any aElement = xLibContainer->getByName( sLibName ); + aElement >>= xDialogLib; + } + + if ( !xDialogLib.is() ) + { + throw IllegalArgumentException( + "DialogProviderImpl::getDialogModel: library not found!", + Reference< XInterface >(), 1 ); + } + + // get input stream provider + if ( xDialogLib->hasByName( sDlgName ) ) + { + Any aElement = xDialogLib->getByName( sDlgName ); + aElement >>= xISP; + } + + if ( !xISP.is() ) + { + throw IllegalArgumentException( + "DialogProviderImpl::getDialogModel: dialog not found!", + Reference< XInterface >(), 1 ); + } + + + + if ( xISP.is() ) + xInput = xISP->createInputStream(); + msDialogLibName = sLibName; + } + + // import dialog model + Reference< XControlModel > xCtrlModel; + if ( xInput.is() && m_xContext.is() ) + { + Reference< resource::XStringResourceManager > xStringResourceManager; + if( bSingleDialog ) + { + xStringResourceManager = lcl_getStringResourceManager(m_xContext,aURL); + } + else if( xDialogLib.is() ) + { + xStringResourceManager = getStringResourceFromDialogLibrary( xDialogLib ); + } + + Any aDialogSourceURLAny; + aDialogSourceURLAny <<= aURL; + + Reference< container::XNameContainer > xDialogModel( createDialogModel( xInput , xStringResourceManager, aDialogSourceURLAny ), UNO_SET_THROW); + + xCtrlModel.set( xDialogModel, UNO_QUERY ); + } + return xCtrlModel; + } + + + Reference< XUnoControlDialog > DialogProviderImpl::createDialogControl + ( const Reference< XControlModel >& rxDialogModel, const Reference< XWindowPeer >& xParent ) + { + OSL_ENSURE( rxDialogModel.is(), "DialogProviderImpl::getDialogControl: no dialog model" ); + + Reference< XUnoControlDialog > xDialogControl; + + if ( m_xContext.is() ) + { + xDialogControl = UnoControlDialog::create( m_xContext ); + + // set the model + if ( rxDialogModel.is() ) + xDialogControl->setModel( rxDialogModel ); + + // set visible + xDialogControl->setVisible( false ); + + // get the parent of the dialog control + Reference< XWindowPeer > xPeer; + if( xParent.is() ) + { + xPeer = xParent; + } + else if ( m_xModel.is() ) + { + Reference< frame::XController > xController = m_xModel->getCurrentController(); + if ( xController.is() ) + { + Reference< frame::XFrame > xFrame = xController->getFrame(); + if ( xFrame.is() ) + xPeer.set( xFrame->getContainerWindow(), UNO_QUERY ); + } + } + + // create a peer + Reference< XToolkit> xToolkit( Toolkit::create( m_xContext ), UNO_QUERY_THROW ); + xDialogControl->createPeer( xToolkit, xPeer ); + } + + return xDialogControl; + } + + + void DialogProviderImpl::attachControlEvents( + const Reference< XControl >& rxControl, + const Reference< XInterface >& rxHandler, + const Reference< XIntrospectionAccess >& rxIntrospectionAccess, + bool bDialogProviderMode ) + { + if ( !rxControl.is() ) + return; + + Reference< XControlContainer > xControlContainer( rxControl, UNO_QUERY ); + + if ( !xControlContainer.is() ) + return; + + Sequence< Reference< XControl > > aControls = xControlContainer->getControls(); + const Reference< XControl >* pControls = aControls.getConstArray(); + sal_Int32 nControlCount = aControls.getLength(); + + Sequence< Reference< XInterface > > aObjects( nControlCount + 1 ); + Reference< XInterface >* pObjects = aObjects.getArray(); + for ( sal_Int32 i = 0; i < nControlCount; ++i ) + { + pObjects[i].set( pControls[i], UNO_QUERY ); + } + + // also add the dialog control itself to the sequence + pObjects[nControlCount].set( rxControl, UNO_QUERY ); + + Reference<XScriptEventsAttacher> xScriptEventsAttacher + = new DialogEventsAttacherImpl( + m_xContext, m_xModel, rxControl, rxHandler, rxIntrospectionAccess, + bDialogProviderMode, + (m_BasicInfo ? m_BasicInfo->mxBasicRTLListener : nullptr), msDialogLibName); + + Any aHelper; + xScriptEventsAttacher->attachEvents( aObjects, Reference< XScriptListener >(), aHelper ); + } + + Reference< XIntrospectionAccess > DialogProviderImpl::inspectHandler( const Reference< XInterface >& rxHandler ) + { + Reference< XIntrospectionAccess > xIntrospectionAccess; + static Reference< XIntrospection > xIntrospection; + + if( !rxHandler.is() ) + return xIntrospectionAccess; + + if( !xIntrospection.is() ) + { + // Get introspection service + xIntrospection = theIntrospection::get( m_xContext ); + } + + // Do introspection + try + { + Any aHandlerAny; + aHandlerAny <<= rxHandler; + xIntrospectionAccess = xIntrospection->inspect( aHandlerAny ); + } + catch( RuntimeException& ) + { + xIntrospectionAccess.clear(); + } + return xIntrospectionAccess; + } + + + // XServiceInfo + + + OUString DialogProviderImpl::getImplementationName( ) + { + return "com.sun.star.comp.scripting.DialogProvider"; + } + + sal_Bool DialogProviderImpl::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + Sequence< OUString > DialogProviderImpl::getSupportedServiceNames( ) + { + return { "com.sun.star.awt.DialogProvider", + "com.sun.star.awt.DialogProvider2", + "com.sun.star.awt.ContainerWindowProvider" }; + } + + + // XInitialization + + + void DialogProviderImpl::initialize( const Sequence< Any >& aArguments ) + { + ::osl::MutexGuard aGuard( getMutex() ); + + if ( aArguments.getLength() == 1 ) + { + aArguments[0] >>= m_xModel; + + if ( !m_xModel.is() ) + { + throw RuntimeException( "DialogProviderImpl::initialize: invalid argument format!" ); + } + } + else if ( aArguments.getLength() == 4 ) + { + // call from RTL_Impl_CreateUnoDialog + aArguments[0] >>= m_xModel; + m_BasicInfo.reset( new BasicRTLParams ); + m_BasicInfo->mxInput.set( aArguments[ 1 ], UNO_QUERY_THROW ); + // allow null mxDlgLib, a document dialog instantiated from + // from application basic is unable to provide ( or find ) it's + // Library + aArguments[ 2 ] >>= m_BasicInfo->mxDlgLib; + // leave the possibility to optionally allow the old dialog creation + // to use the new XScriptListener ( which converts the old style macro + // to a SF url ) + m_BasicInfo->mxBasicRTLListener.set( aArguments[ 3 ], UNO_QUERY); + } + else if ( aArguments.getLength() > 4 ) + { + throw RuntimeException( "DialogProviderImpl::initialize: invalid number of arguments!" ); + } + } + + + // XDialogProvider + + + constexpr OUStringLiteral aDecorationPropName = u"Decoration"; + + Reference < XControl > DialogProviderImpl::createDialogImpl( + const OUString& URL, const Reference< XInterface >& xHandler, + const Reference< XWindowPeer >& xParent, bool bDialogProviderMode ) + { + // if the dialog is located in a document, the document must already be open! + + ::osl::MutexGuard aGuard( getMutex() ); + + + // m_xHandler = xHandler; + + //Reference< XDialog > xDialog; + Reference< XControl > xCtrl; + Reference< XControlModel > xCtrlMod; + try + { + // add support for basic RTL_FUNCTION + if (m_BasicInfo) + xCtrlMod = createDialogModelForBasic(); + else + { + OSL_ENSURE( !URL.isEmpty(), "DialogProviderImpl::getDialog: no URL!" ); + xCtrlMod = createDialogModel( URL ); + } + } + catch ( const RuntimeException& ) { throw; } + catch ( const Exception& ) + { + const Any aError( ::cppu::getCaughtException() ); + throw WrappedTargetRuntimeException( OUString(), *this, aError ); + } + if ( xCtrlMod.is() ) + { + // i83963 Force decoration + if( bDialogProviderMode ) + { + uno::Reference< beans::XPropertySet > xDlgModPropSet( xCtrlMod, uno::UNO_QUERY ); + if( xDlgModPropSet.is() ) + { + try + { + bool bDecoration = true; + Any aDecorationAny = xDlgModPropSet->getPropertyValue( aDecorationPropName ); + aDecorationAny >>= bDecoration; + if( !bDecoration ) + { + xDlgModPropSet->setPropertyValue( aDecorationPropName, Any( true ) ); + xDlgModPropSet->setPropertyValue( "Title", Any( OUString() ) ); + } + } + catch( UnknownPropertyException& ) + {} + } + } + + xCtrl.set( createDialogControl( xCtrlMod, xParent ) ); + if ( xCtrl.is() ) + { + Reference< XIntrospectionAccess > xIntrospectionAccess = inspectHandler( xHandler ); + attachControlEvents( xCtrl, xHandler, xIntrospectionAccess, bDialogProviderMode ); + } + } + + return xCtrl; + } + + Reference < XDialog > DialogProviderImpl::createDialog( const OUString& URL ) + { + Reference< XInterface > xDummyHandler; + Reference< XWindowPeer > xDummyPeer; + Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xDummyHandler, xDummyPeer, true ); + Reference< XDialog > xDialog( xControl, UNO_QUERY ); + return xDialog; + } + + Reference < XDialog > DialogProviderImpl::createDialogWithHandler( + const OUString& URL, const Reference< XInterface >& xHandler ) + { + if( !xHandler.is() ) + { + throw IllegalArgumentException( + "DialogProviderImpl::createDialogWithHandler: Invalid xHandler!", + Reference< XInterface >(), 1 ); + } + Reference< XWindowPeer > xDummyPeer; + Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xHandler, xDummyPeer, true ); + Reference< XDialog > xDialog( xControl, UNO_QUERY ); + return xDialog; + } + + Reference < XDialog > DialogProviderImpl::createDialogWithArguments( + const OUString& URL, const Sequence< NamedValue >& Arguments ) + { + ::comphelper::NamedValueCollection aArguments( Arguments ); + + Reference< XWindowPeer > xParentPeer; + if ( aArguments.has( "ParentWindow" ) ) + { + const Any& aParentWindow( aArguments.get( "ParentWindow" ) ); + if ( !( aParentWindow >>= xParentPeer ) ) + { + const Reference< XControl > xParentControl( aParentWindow, UNO_QUERY ); + if ( xParentControl.is() ) + xParentPeer = xParentControl->getPeer(); + } + } + + const Reference< XInterface > xHandler( aArguments.get( "EventHandler" ), UNO_QUERY ); + + Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xHandler, xParentPeer, true ); + Reference< XDialog > xDialog( xControl, UNO_QUERY ); + return xDialog; + } + + Reference< XWindow > DialogProviderImpl::createContainerWindow( + const OUString& URL, const OUString&, + const Reference< XWindowPeer >& xParent, const Reference< XInterface >& xHandler ) + { + if( !xParent.is() ) + { + throw IllegalArgumentException( + "DialogProviderImpl::createContainerWindow: Invalid xParent!", + Reference< XInterface >(), 1 ); + } + Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xHandler, xParent, false ); + Reference< XWindow> xWindow( xControl, UNO_QUERY ); + return xWindow; + } + + + // component operations + + + extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* + scripting_DialogProviderImpl_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) + { + return cppu::acquire(new DialogProviderImpl(context)); + } + +} // namespace dlgprov + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/dlgprov/dlgprov.hxx b/scripting/source/dlgprov/dlgprov.hxx new file mode 100644 index 000000000..0a5d4483a --- /dev/null +++ b/scripting/source/dlgprov/dlgprov.hxx @@ -0,0 +1,148 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XDialog.hpp> +#include <com/sun/star/awt/XDialogProvider2.hpp> +#include <com/sun/star/awt/XContainerWindowProvider.hpp> +#include <com/sun/star/awt/XUnoControlDialog.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/resource/XStringResourceManager.hpp> +#include <com/sun/star/script/XScriptListener.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/mutex.hxx> +#include <memory> + + +namespace dlgprov +{ + + + // mutex + + + ::osl::Mutex& getMutex(); + + + + css::uno::Reference< css::container::XNameContainer > lcl_createControlModel(const css::uno::Reference< css::uno::XComponentContext >& i_xContext); + css::uno::Reference< css::resource::XStringResourceManager > lcl_getStringResourceManager(const css::uno::Reference< css::uno::XComponentContext >& i_xContext, std::u16string_view i_sURL); + /// @throws css::uno::Exception + css::uno::Reference< css::container::XNameContainer > lcl_createDialogModel( + const css::uno::Reference< css::uno::XComponentContext >& i_xContext, + const css::uno::Reference< css::io::XInputStream >& xInput, + const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager, + const css::uno::Any &aDialogSourceURL); + + typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::awt::XDialogProvider2, + css::awt::XContainerWindowProvider > DialogProviderImpl_BASE; + + class DialogProviderImpl : public DialogProviderImpl_BASE + { + private: + struct BasicRTLParams + { + css::uno::Reference< css::io::XInputStream > mxInput; + css::uno::Reference< css::container::XNameContainer > mxDlgLib; + css::uno::Reference< css::script::XScriptListener > mxBasicRTLListener; + }; + std::unique_ptr< BasicRTLParams > m_BasicInfo; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::frame::XModel > m_xModel; + + OUString msDialogLibName; + css::uno::Reference< css::awt::XControlModel > createDialogModel( const OUString& sURL ); + + css::uno::Reference< css::awt::XUnoControlDialog > createDialogControl( + const css::uno::Reference< css::awt::XControlModel >& rxDialogModel, + const css::uno::Reference< css::awt::XWindowPeer >& xParent ); + + void attachControlEvents( const css::uno::Reference< css::awt::XControl >& rxControlContainer, + const css::uno::Reference< css::uno::XInterface >& rxHandler, + const css::uno::Reference< css::beans::XIntrospectionAccess >& rxIntrospectionAccess, + bool bDialogProviderMode ); + css::uno::Reference< css::beans::XIntrospectionAccess > inspectHandler( + const css::uno::Reference< css::uno::XInterface >& rxHandler ); + // helper methods + /// @throws css::uno::Exception + css::uno::Reference< css::container::XNameContainer > createDialogModel( + const css::uno::Reference< css::io::XInputStream >& xInput, + const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager, + const css::uno::Any &aDialogSourceURL); + /// @throws css::uno::Exception + css::uno::Reference< css::awt::XControlModel > createDialogModelForBasic(); + + // XDialogProvider / XDialogProvider2 impl method + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + css::uno::Reference < css::awt::XControl > createDialogImpl( + const OUString& URL, + const css::uno::Reference< css::uno::XInterface >& xHandler, + const css::uno::Reference< css::awt::XWindowPeer >& xParent, + bool bDialogProviderMode ); + + public: + explicit DialogProviderImpl( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~DialogProviderImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XDialogProvider + virtual css::uno::Reference < css::awt::XDialog > SAL_CALL createDialog( + const OUString& URL ) override; + + // XDialogProvider2 + virtual css::uno::Reference < css::awt::XDialog > SAL_CALL createDialogWithHandler( + const OUString& URL, + const css::uno::Reference< css::uno::XInterface >& xHandler ) override; + + virtual css::uno::Reference < css::awt::XDialog > SAL_CALL createDialogWithArguments( + const OUString& URL, + const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override; + + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createContainerWindow( + const OUString& URL, const OUString& WindowType, + const css::uno::Reference< css::awt::XWindowPeer >& xParent, + const css::uno::Reference< css::uno::XInterface >& xHandler ) override; + }; + + +} // namespace dlgprov + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/inc/bcholder.hxx b/scripting/source/inc/bcholder.hxx new file mode 100644 index 000000000..9f8add31b --- /dev/null +++ b/scripting/source/inc/bcholder.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#pragma once + +#include <osl/mutex.hxx> +#include <cppuhelper/interfacecontainer.h> + + +namespace scripting_helper +{ + + class OBroadcastHelperHolder + { + ::cppu::OBroadcastHelper m_aBHelper; + + public: + OBroadcastHelperHolder( ::osl::Mutex& rMutex ) : m_aBHelper( rMutex ) { } + + ::cppu::OBroadcastHelper& GetBroadcastHelper() { return m_aBHelper; } + const ::cppu::OBroadcastHelper& GetBroadcastHelper() const { return m_aBHelper; } + }; + + +} // namespace scripting_helper + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/inc/util/MiscUtils.hxx b/scripting/source/inc/util/MiscUtils.hxx new file mode 100644 index 000000000..2ba4492ab --- /dev/null +++ b/scripting/source/inc/util/MiscUtils.hxx @@ -0,0 +1,138 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <osl/diagnose.h> + +#include <ucbhelper/content.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <comphelper/processfactory.hxx> + +namespace sf_misc +{ + +class MiscUtils +{ +public: + +static css::uno::Sequence< OUString > allOpenTDocUrls( const css::uno::Reference< css::uno::XComponentContext >& xCtx) +{ + css::uno::Sequence< OUString > result; + try + { + if ( !xCtx.is() ) + { + return result; + } + css::uno::Reference < css::ucb::XSimpleFileAccess3 > xSFA( css::ucb::SimpleFileAccess::create(xCtx) ); + result = xSFA->getFolderContents( "vnd.sun.star.tdoc:/", true ); + } + catch ( css::uno::Exception& ) + { + } + return result; +} + +static OUString xModelToTdocUrl( const css::uno::Reference< css::frame::XModel >& xModel, + const css::uno::Reference< css::uno::XComponentContext >& xContext ) +{ + css::uno::Reference< css::lang::XMultiComponentFactory > xMCF( + xContext->getServiceManager() ); + css::uno::Reference< + css::frame::XTransientDocumentsDocumentContentFactory > xDocFac; + try + { + xDocFac.set(xMCF->createInstanceWithContext( + "com.sun.star.frame.TransientDocumentsDocumentContentFactory", + xContext ), + css::uno::UNO_QUERY ); + } + catch ( css::uno::Exception const & ) + { + // handled below + } + + if ( xDocFac.is() ) + { + try + { + css::uno::Reference< css::ucb::XContent > xContent( + xDocFac->createDocumentContent( xModel ) ); + return xContent->getIdentifier()->getContentIdentifier(); + } + catch ( css::lang::IllegalArgumentException const & ) + { + OSL_FAIL( "Invalid document model!" ); + } + } + + OSL_FAIL( "Unable to obtain URL for document model!" ); + return OUString(); +} + +static css::uno::Reference< css::frame::XModel > tDocUrlToModel( const OUString& url ) +{ + css::uno::Any result; + + try + { + ::ucbhelper::Content root( url, nullptr, comphelper::getProcessComponentContext() ); + result = getUCBProperty( root, "DocumentModel" ); + } + catch ( css::ucb::ContentCreationException& ) + { + // carry on, empty value will be returned + } + catch ( css::uno::RuntimeException& ) + { + // carry on, empty value will be returned + } + + css::uno::Reference< css::frame::XModel > xModel( + result, css::uno::UNO_QUERY ); + + return xModel; +} + + +static css::uno::Any getUCBProperty( ::ucbhelper::Content& content, OUString const & prop ) +{ + css::uno::Any result; + try + { + result = content.getPropertyValue( prop ); + } + catch ( css::uno::Exception& ) + { + } + return result; +} + +}; +} // namespace sf_misc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/protocolhandler/protocolhandler.component b/scripting/source/protocolhandler/protocolhandler.component new file mode 100644 index 000000000..959c332c0 --- /dev/null +++ b/scripting/source/protocolhandler/protocolhandler.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.ScriptProtocolHandler" + constructor="scripting_ScriptProtocolHandler_get_implementation"> + <service name="com.sun.star.frame.ProtocolHandler"/> + </implementation> +</component> diff --git a/scripting/source/protocolhandler/scripthandler.cxx b/scripting/source/protocolhandler/scripthandler.cxx new file mode 100644 index 000000000..08ec8d302 --- /dev/null +++ b/scripting/source/protocolhandler/scripthandler.cxx @@ -0,0 +1,436 @@ +/* -*- 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 "scripthandler.hxx" + +#include <com/sun/star/frame/DispatchResultEvent.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp> + +#include <sfx2/objsh.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/sfxdlg.hxx> +#include <tools/diagnose_ex.h> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <framework/documentundoguard.hxx> +#include <officecfg/Office/Common.hxx> + +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::script::provider; +using namespace ::com::sun::star::document; + +namespace scripting_protocolhandler +{ + +void SAL_CALL ScriptProtocolHandler::initialize( + const css::uno::Sequence < css::uno::Any >& aArguments ) +{ + if ( m_bInitialised ) + { + return ; + } + + // first argument contains a reference to the frame (may be empty or the desktop, + // but usually it's a "real" frame) + if ( aArguments.hasElements() && !( aArguments[ 0 ] >>= m_xFrame ) ) + { + throw RuntimeException( "ScriptProtocolHandler::initialize: could not extract reference to the frame" ); + } + + ENSURE_OR_THROW( m_xContext.is(), "ScriptProtocolHandler::initialize: No Service Manager available" ); + m_bInitialised = true; +} + +Reference< XDispatch > SAL_CALL ScriptProtocolHandler::queryDispatch( + const URL& aURL, const OUString&, sal_Int32 ) +{ + Reference< XDispatch > xDispatcher; + // get scheme of url + + Reference< uri::XUriReferenceFactory > xFac = uri::UriReferenceFactory::create( m_xContext ); + Reference< uri::XUriReference > uriRef = xFac->parse( aURL.Complete ); + if ( uriRef.is() ) + { + if ( uriRef->getScheme() == "vnd.sun.star.script" ) + { + xDispatcher = this; + } + } + + return xDispatcher; +} + +Sequence< Reference< XDispatch > > SAL_CALL +ScriptProtocolHandler::queryDispatches( +const Sequence < DispatchDescriptor >& seqDescriptor ) +{ + sal_Int32 nCount = seqDescriptor.getLength(); + Sequence< Reference< XDispatch > > lDispatcher( nCount ); + std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(), + [this](const DispatchDescriptor& rDescr) -> Reference<XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return lDispatcher; +} + +void SAL_CALL ScriptProtocolHandler::dispatchWithNotification( + const URL& aURL, const Sequence < PropertyValue >& lArgs, + const Reference< XDispatchResultListener >& xListener ) +{ + if (officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get()) + return; + + bool bSuccess = false; + Any invokeResult; + bool bCaughtException = false; + Any aException; + + if ( m_bInitialised ) + { + try + { + css::uno::Reference<css::uri::XUriReferenceFactory> urifac( + css::uri::UriReferenceFactory::create(m_xContext)); + css::uno::Reference<css::uri::XVndSunStarScriptUrlReference> uri( + urifac->parse(aURL.Complete), css::uno::UNO_QUERY_THROW); + auto const loc = uri->getParameter("location"); + bool bIsDocumentScript = loc == "document"; + + if ( bIsDocumentScript ) + { + // obtain the component for our security check + Reference< XEmbeddedScripts > xDocumentScripts; + if ( getScriptInvocation() ) + xDocumentScripts.set( m_xScriptInvocation->getScriptContainer(), UNO_SET_THROW ); + + OSL_ENSURE( xDocumentScripts.is(), "ScriptProtocolHandler::dispatchWithNotification: can't do the security check!" ); + if ( !xDocumentScripts.is() || !xDocumentScripts->getAllowMacroExecution() ) + { + if ( xListener.is() ) + { + css::frame::DispatchResultEvent aEvent( + static_cast< ::cppu::OWeakObject* >( this ), + css::frame::DispatchResultState::FAILURE, + invokeResult ); + try + { + xListener->dispatchFinished( aEvent ) ; + } + catch(const RuntimeException &) + { + TOOLS_WARN_EXCEPTION("scripting", + "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException" + "while dispatchFinished with failure of the execution"); + } + } + return; + } + } + + // Creates a ScriptProvider ( if one is not created already ) + createScriptProvider(); + + Reference< provider::XScript > xFunc = + m_xScriptProvider->getScript( aURL.Complete ); + ENSURE_OR_THROW( xFunc.is(), + "ScriptProtocolHandler::dispatchWithNotification: validate xFunc - unable to obtain XScript interface" ); + + + Sequence< Any > inArgs; + Sequence< Any > outArgs; + Sequence< sal_Int16 > outIndex; + + if ( lArgs.hasElements() ) + { + int argCount = 0; + for ( const auto& rArg : lArgs ) + { + // Sometimes we get a propertyval with name = "Referer" or "SynchronMode". These + // are not actual arguments to be passed to script, but flags describing the + // call, so ignore. Who thought that passing such "meta-arguments" mixed in with + // real arguments was a good idea? + if ( (rArg.Name != "Referer" && + rArg.Name != "SynchronMode") || + rArg.Name.isEmpty() ) //TODO:??? + { + inArgs.realloc( ++argCount ); + inArgs.getArray()[ argCount - 1 ] = rArg.Value; + } + } + } + + // attempt to protect the document against the script tampering with its Undo Context + std::unique_ptr< ::framework::DocumentUndoGuard > pUndoGuard; + if ( bIsDocumentScript ) + pUndoGuard.reset( new ::framework::DocumentUndoGuard( m_xScriptInvocation ) ); + + bSuccess = false; + while ( !bSuccess ) + { + std::exception_ptr aFirstCaughtException; + try + { + invokeResult = xFunc->invoke( inArgs, outIndex, outArgs ); + bSuccess = true; + } + catch( const provider::ScriptFrameworkErrorException& se ) + { + if (!aFirstCaughtException) + aFirstCaughtException = std::current_exception(); + + if ( se.errorType != provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT ) + // the only condition which allows us to retry is if there is no method with the + // given name/signature + std::rethrow_exception(aFirstCaughtException); + + if ( !inArgs.hasElements() ) + // no chance to retry if we can't strip more in-args + std::rethrow_exception(aFirstCaughtException); + + // strip one argument, then retry + inArgs.realloc( inArgs.getLength() - 1 ); + } + } + } + // Office doesn't handle exceptions rethrown here very well, it cores, + // all we can is log them and then set fail for the dispatch event! + // (if there is a listener of course) + catch ( const Exception & e ) + { + aException = ::cppu::getCaughtException(); + + invokeResult <<= "ScriptProtocolHandler::dispatch: caught " + + aException.getValueTypeName() + ": " + e.Message; + + bCaughtException = true; + } + } + else + { + invokeResult <<= OUString( + "ScriptProtocolHandler::dispatchWithNotification failed, ScriptProtocolHandler not initialised" + ); + } + + if ( bCaughtException ) + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + pFact->ShowAsyncScriptErrorDialog( nullptr, aException ); + } + + if ( !xListener.is() ) + return; + + // always call dispatchFinished(), because we didn't load a document but + // executed a macro instead! + css::frame::DispatchResultEvent aEvent; + + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + aEvent.Result = invokeResult; + if ( bSuccess ) + { + aEvent.State = css::frame::DispatchResultState::SUCCESS; + } + else + { + aEvent.State = css::frame::DispatchResultState::FAILURE; + } + + try + { + xListener->dispatchFinished( aEvent ) ; + } + catch(const RuntimeException &) + { + TOOLS_WARN_EXCEPTION("scripting", + "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException" + "while dispatchFinished" ); + } +} + +void SAL_CALL ScriptProtocolHandler::dispatch( +const URL& aURL, const Sequence< PropertyValue >& lArgs ) +{ + dispatchWithNotification( aURL, lArgs, Reference< XDispatchResultListener >() ); +} + +void SAL_CALL ScriptProtocolHandler::addStatusListener( +const Reference< XStatusListener >&, const URL& ) +{ + // implement if status is supported +} + +void SAL_CALL ScriptProtocolHandler::removeStatusListener( +const Reference< XStatusListener >&, const URL& ) +{} + +bool +ScriptProtocolHandler::getScriptInvocation() +{ + if ( !m_xScriptInvocation.is() && m_xFrame.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + if ( xController .is() ) + { + // try to obtain an XScriptInvocationContext interface, preferred from the + // mode, then from the controller + if ( !m_xScriptInvocation.set( xController->getModel(), UNO_QUERY ) ) + m_xScriptInvocation.set( xController, UNO_QUERY ); + } + else + { + if ( m_xFrame.is() ) + { + SfxFrame* pFrame = nullptr; + for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) ) + { + if ( pFrame->GetFrameInterface() == m_xFrame ) + break; + } + if (SfxObjectShell* pDocShell = pFrame ? pFrame->GetCurrentDocument() : SfxObjectShell::Current()) + { + Reference< XModel > xModel( pDocShell->GetModel() ); + m_xScriptInvocation.set( xModel, UNO_QUERY ); + } + } + } + } + return m_xScriptInvocation.is(); +} + +void ScriptProtocolHandler::createScriptProvider() +{ + if ( m_xScriptProvider.is() ) + return; + + try + { + // first, ask the component supporting the XScriptInvocationContext interface + // (if there is one) for a script provider + if ( getScriptInvocation() ) + { + Reference< XScriptProviderSupplier > xSPS( m_xScriptInvocation, UNO_QUERY ); + if ( xSPS.is() ) + m_xScriptProvider = xSPS->getScriptProvider(); + } + + // second, ask the model in our frame + if ( !m_xScriptProvider.is() && m_xFrame.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + if ( xController .is() ) + { + Reference< XScriptProviderSupplier > xSPS( xController->getModel(), UNO_QUERY ); + if ( xSPS.is() ) + m_xScriptProvider = xSPS->getScriptProvider(); + } + } + + + // as a fallback, ask the controller + if ( !m_xScriptProvider.is() && m_xFrame.is() ) + { + Reference< XScriptProviderSupplier > xSPS( m_xFrame->getController(), UNO_QUERY ); + if ( xSPS.is() ) + m_xScriptProvider = xSPS->getScriptProvider(); + } + + // if nothing of this is successful, use the master script provider + if ( !m_xScriptProvider.is() ) + { + Reference< provider::XScriptProviderFactory > xFac = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + Any aContext; + if ( getScriptInvocation() ) + aContext <<= m_xScriptInvocation; + m_xScriptProvider.set( xFac->createScriptProvider( aContext ), UNO_SET_THROW ); + } + } + catch ( const Exception & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "ScriptProtocolHandler::createScriptProvider: " + e.Message, + nullptr, anyEx ); + } +} + +ScriptProtocolHandler::ScriptProtocolHandler( const Reference< css::uno::XComponentContext > & xContext ) + : m_bInitialised( false ), m_xContext( xContext ) +{ +} + +ScriptProtocolHandler::~ScriptProtocolHandler() +{ +} + +/* XServiceInfo */ +OUString SAL_CALL ScriptProtocolHandler::getImplementationName( ) +{ + return "com.sun.star.comp.ScriptProtocolHandler"; +} + +/* XServiceInfo */ +sal_Bool SAL_CALL ScriptProtocolHandler::supportsService(const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +/* XServiceInfo */ +Sequence< OUString > SAL_CALL ScriptProtocolHandler::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ProtocolHandler"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_ScriptProtocolHandler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new ScriptProtocolHandler(context)); +} + +} // namespace scripting_protocolhandler + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/protocolhandler/scripthandler.hxx b/scripting/source/protocolhandler/scripthandler.hxx new file mode 100644 index 000000000..74667b5a4 --- /dev/null +++ b/scripting/source/protocolhandler/scripthandler.hxx @@ -0,0 +1,114 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/script/provider/XScriptProvider.hpp> + + +namespace com::sun::star { + + namespace document { + class XScriptInvocationContext; + } + namespace uno { + class Any; + class XComponentContext; + } + namespace lang { + class XMultiServiceFactory; + class XSingleServiceFactory; + } + namespace frame { + class XFrame; + class XDispatch; + class XNotifyingDispatch; + class XDispatchResultListener; + struct DispatchDescriptor; + } + namespace beans { + struct PropertyValue; + } + namespace util { + struct URL; + } +} + +namespace scripting_protocolhandler +{ + +class ScriptProtocolHandler : +public ::cppu::WeakImplHelper< css::frame::XDispatchProvider, + css::frame::XNotifyingDispatch, css::lang::XServiceInfo, css::lang::XInitialization > +{ +private: + bool m_bInitialised; + css::uno::Reference < css::uno::XComponentContext > m_xContext; + css::uno::Reference < css::frame::XFrame > m_xFrame; + css::uno::Reference < css::script::provider::XScriptProvider > m_xScriptProvider; + css::uno::Reference< css::document::XScriptInvocationContext > m_xScriptInvocation; + + void createScriptProvider(); + bool getScriptInvocation(); + +public: + explicit ScriptProtocolHandler( const css::uno::Reference < css::uno::XComponentContext >& xContext ); + virtual ~ScriptProtocolHandler() override; + + /* XServiceInfo */ + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override; + virtual css::uno::Sequence < OUString > SAL_CALL getSupportedServiceNames() override; + + /* Implementation for XDispatchProvider */ + virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL + queryDispatch( const css::util::URL& aURL, const OUString& sTargetFrameName, + sal_Int32 eSearchFlags ) override ; + virtual css::uno::Sequence< css::uno::Reference < css::frame::XDispatch > > SAL_CALL + queryDispatches( + const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescriptor ) override; + + /* Implementation for X(Notifying)Dispatch */ + virtual void SAL_CALL dispatchWithNotification( + const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArgs, + const css::uno::Reference< css::frame::XDispatchResultListener >& Listener ) override; + virtual void SAL_CALL dispatch( + const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) override; + virtual void SAL_CALL addStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& xControl, + const css::util::URL& aURL ) override; + virtual void SAL_CALL removeStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& xControl, + const css::util::URL& aURL ) override; + + /* Implementation for XInitialization */ + virtual void SAL_CALL initialize( + const css::uno::Sequence < css::uno::Any >& aArguments ) override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/ActiveMSPList.cxx b/scripting/source/provider/ActiveMSPList.cxx new file mode 100644 index 000000000..c073c73b7 --- /dev/null +++ b/scripting/source/provider/ActiveMSPList.cxx @@ -0,0 +1,297 @@ +/* -*- 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/exc_hlp.hxx> +#include <util/MiscUtils.hxx> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> + +#include "ActiveMSPList.hxx" + +#include <tools/diagnose_ex.h> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::script; +using namespace ::sf_misc; + +namespace func_provider +{ + +ActiveMSPList::ActiveMSPList( const Reference< XComponentContext > & xContext ) : m_xContext( xContext ) +{ + userDirString = "user"; + shareDirString = "share"; + bundledDirString = "bundled"; +} + +ActiveMSPList::~ActiveMSPList() +{ +} + +Reference< provider::XScriptProvider > +ActiveMSPList::createNewMSP( const uno::Any& context ) +{ + Sequence< Any > args( &context, 1 ); + + Reference< provider::XScriptProvider > msp( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.script.provider.MasterScriptProvider", args, m_xContext ), UNO_QUERY ); + return msp; +} + +class NonDocMSPCreator +{ +public: + explicit NonDocMSPCreator(ActiveMSPList *pList) + { + pList->createNonDocMSPs(); + } +}; + +namespace +{ + //thread-safe double-locked class to ensure createNonDocMSPs is called once + class theNonDocMSPCreator : public rtl::StaticWithArg<NonDocMSPCreator, ActiveMSPList*, theNonDocMSPCreator> {}; + + void ensureNonDocMSPs(ActiveMSPList *pList) + { + theNonDocMSPCreator::get(pList); + } +} + +Reference< provider::XScriptProvider > +ActiveMSPList::getMSPFromAnyContext( const Any& aContext ) +{ + Reference< provider::XScriptProvider > msp; + OUString sContext; + if ( aContext >>= sContext ) + { + msp = getMSPFromStringContext( sContext ); + return msp; + } + + Reference< frame::XModel > xModel( aContext, UNO_QUERY ); + + Reference< document::XScriptInvocationContext > xScriptContext( aContext, UNO_QUERY ); + if ( xScriptContext.is() ) + { + try + { + // the component supports executing scripts embedded in a - possibly foreign document. + // Check whether this other document it's the component itself. + if ( !xModel.is() || ( xModel != xScriptContext->getScriptContainer() ) ) + { + msp = getMSPFromInvocationContext( xScriptContext ); + return msp; + } + } + catch( const lang::IllegalArgumentException& ) + { + xModel.set( Reference< frame::XModel >() ); + } + } + + if ( xModel.is() ) + { + sContext = MiscUtils::xModelToTdocUrl( xModel, m_xContext ); + msp = getMSPFromStringContext( sContext ); + return msp; + } + + ensureNonDocMSPs(this); + return m_hMsps[ shareDirString ]; +} + +Reference< provider::XScriptProvider > + ActiveMSPList::getMSPFromInvocationContext( const Reference< document::XScriptInvocationContext >& xContext ) +{ + Reference< provider::XScriptProvider > msp; + + Reference< document::XEmbeddedScripts > xScripts; + if ( xContext.is() ) + xScripts.set( xContext->getScriptContainer() ); + if ( !xScripts.is() ) + { + throw lang::IllegalArgumentException( + "Failed to create MasterScriptProvider for ScriptInvocationContext: " + "Component supporting XEmbeddScripts interface not found.", + nullptr, 1 ); + } + + ::osl::MutexGuard guard( m_mutex ); + + Reference< XInterface > xNormalized( xContext, UNO_QUERY ); + ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized ); + if ( pos == m_mScriptComponents.end() ) + { + // TODO + msp = createNewMSP( uno::Any( xContext ) ); + addActiveMSP( xNormalized, msp ); + } + else + { + msp = pos->second; + } + + return msp; +} + +Reference< provider::XScriptProvider > + ActiveMSPList::getMSPFromStringContext( const OUString& context ) +{ + Reference< provider::XScriptProvider > msp; + try + { + if ( context.startsWith( "vnd.sun.star.tdoc" ) ) + { + Reference< frame::XModel > xModel( MiscUtils::tDocUrlToModel( context ) ); + + Reference< document::XEmbeddedScripts > xScripts( xModel, UNO_QUERY ); + Reference< document::XScriptInvocationContext > xScriptsContext( xModel, UNO_QUERY ); + if ( !xScripts.is() && !xScriptsContext.is() ) + { + throw lang::IllegalArgumentException( + "Failed to create MasterScriptProvider for '" + + context + + "': Either XEmbeddScripts or XScriptInvocationContext need to be supported by the document.", + nullptr, 1 ); + } + + ::osl::MutexGuard guard( m_mutex ); + Reference< XInterface > xNormalized( xModel, UNO_QUERY ); + ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized ); + if ( pos == m_mScriptComponents.end() ) + { + msp = createNewMSP( context ); + addActiveMSP( xNormalized, msp ); + } + else + { + msp = pos->second; + } + } + else + { + ::osl::MutexGuard guard( m_mutex ); + Msp_hash::iterator h_itEnd = m_hMsps.end(); + Msp_hash::const_iterator itr = m_hMsps.find( context ); + if ( itr == h_itEnd ) + { + msp = createNewMSP( context ); + m_hMsps[ context ] = msp; + } + else + { + msp = m_hMsps[ context ]; + } + } + } + catch( const lang::IllegalArgumentException& ) + { + // allowed to leave + } + catch( const RuntimeException& ) + { + // allowed to leave + } + catch( const Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "Failed to create MasterScriptProvider for context '" + + context + "'.", + *this, anyEx ); + } + return msp; +} + +void +ActiveMSPList::addActiveMSP( const Reference< uno::XInterface >& xComponent, + const Reference< provider::XScriptProvider >& msp ) +{ + ::osl::MutexGuard guard( m_mutex ); + Reference< XInterface > xNormalized( xComponent, UNO_QUERY ); + ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized ); + if ( pos != m_mScriptComponents.end() ) + return; + + m_mScriptComponents[ xNormalized ] = msp; + + // add self as listener for component disposal + // should probably throw from this method!!, reexamine + try + { + Reference< lang::XComponent > xBroadcaster( xComponent, UNO_QUERY_THROW ); + xBroadcaster->addEventListener( this ); + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } +} + + +void SAL_CALL ActiveMSPList::disposing( const css::lang::EventObject& Source ) + +{ + try + { + Reference< XInterface > xNormalized( Source.Source, UNO_QUERY ); + if ( xNormalized.is() ) + { + ::osl::MutexGuard guard( m_mutex ); + ScriptComponent_map::iterator pos = m_mScriptComponents.find( xNormalized ); + if ( pos != m_mScriptComponents.end() ) + m_mScriptComponents.erase( pos ); + } + } + catch ( const Exception& ) + { + // if we get an exception here, there is not much we can do about + // it can't throw as it will screw up the model that is calling dispose + DBG_UNHANDLED_EXCEPTION("scripting"); + } +} + +void +ActiveMSPList::createNonDocMSPs() +{ + // do creation of user and share MSPs here + OUString serviceName("com.sun.star.script.provider.MasterScriptProvider"); + + Sequence< Any > args{ Any(userDirString) }; + Reference< provider::XScriptProvider > userMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY ); + // should check if provider reference is valid + m_hMsps[ userDirString ] = userMsp; + + args = { Any(shareDirString) }; + Reference< provider::XScriptProvider > shareMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY ); + // should check if provider reference is valid + m_hMsps[ shareDirString ] = shareMsp; + + args = { Any(bundledDirString) }; + Reference< provider::XScriptProvider > bundledMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY ); + // should check if provider reference is valid + m_hMsps[ bundledDirString ] = bundledMsp; +} + +} // namespace func_provider + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/ActiveMSPList.hxx b/scripting/source/provider/ActiveMSPList.hxx new file mode 100644 index 000000000..fb52629c1 --- /dev/null +++ b/scripting/source/provider/ActiveMSPList.hxx @@ -0,0 +1,94 @@ +/* -*- 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 . + */ +#pragma once + +#include <osl/mutex.hxx> +#include <rtl/ustring.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/lang/XEventListener.hpp> + +#include <com/sun/star/script/provider/XScriptProvider.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <map> +#include <unordered_map> + +namespace func_provider +{ + +//Typedefs +typedef std::map < css::uno::Reference< css::uno::XInterface > + , css::uno::Reference< css::script::provider::XScriptProvider > + > ScriptComponent_map; + +typedef std::unordered_map< OUString, + css::uno::Reference< css::script::provider::XScriptProvider > > Msp_hash; + +class NonDocMSPCreator; + +class ActiveMSPList : public ::cppu::WeakImplHelper< css::lang::XEventListener > +{ + +public: + + explicit ActiveMSPList( const css::uno::Reference< + css::uno::XComponentContext > & xContext ); + virtual ~ActiveMSPList() override; + + css::uno::Reference< css::script::provider::XScriptProvider > + getMSPFromStringContext( const OUString& context ); + + css::uno::Reference< css::script::provider::XScriptProvider > + getMSPFromAnyContext( const css::uno::Any& context ); + + css::uno::Reference< css::script::provider::XScriptProvider > + getMSPFromInvocationContext( const css::uno::Reference< css::document::XScriptInvocationContext >& context ); + + //XEventListener + + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + void addActiveMSP( const css::uno::Reference< css::uno::XInterface >& xComponent, + const css::uno::Reference< css::script::provider::XScriptProvider >& msp ); + css::uno::Reference< css::script::provider::XScriptProvider > + createNewMSP( const css::uno::Any& context ); + css::uno::Reference< css::script::provider::XScriptProvider > + createNewMSP( const OUString& context ) + { + return createNewMSP( css::uno::Any( context ) ); + } + + friend class NonDocMSPCreator; + void createNonDocMSPs(); + + Msp_hash m_hMsps; + ScriptComponent_map m_mScriptComponents; + osl::Mutex m_mutex; + OUString userDirString; + OUString shareDirString; + OUString bundledDirString; + css::uno::Reference< css::uno::XComponentContext > m_xContext; +}; +} // func_provider + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/BrowseNodeFactoryImpl.cxx b/scripting/source/provider/BrowseNodeFactoryImpl.cxx new file mode 100644 index 000000000..9d070ab00 --- /dev/null +++ b/scripting/source/provider/BrowseNodeFactoryImpl.cxx @@ -0,0 +1,650 @@ +/* -*- 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/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/mediadescriptor.hxx> + +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/reflection/ProxyFactory.hpp> + +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp> +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> + +#include <tools/diagnose_ex.h> + +#include "BrowseNodeFactoryImpl.hxx" +#include <util/MiscUtils.hxx> + +#include <vector> +#include <algorithm> +#include <memory> +#include <optional> +#include <string_view> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::sf_misc; + +namespace browsenodefactory +{ +namespace { +class BrowseNodeAggregator : + public ::cppu::WeakImplHelper< browse::XBrowseNode > +{ +private: + OUString m_Name; + std::vector< Reference< browse::XBrowseNode > > m_Nodes; + +public: + + explicit BrowseNodeAggregator( const Reference< browse::XBrowseNode >& node ) + : m_Name(node->getName()) + { + m_Nodes.resize( 1 ); + m_Nodes[ 0 ] = node; + } + + void addBrowseNode( const Reference< browse::XBrowseNode>& node ) + { + m_Nodes.push_back( node ); + } + + virtual OUString + SAL_CALL getName() override + { + return m_Name; + } + + virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL + getChildNodes() override + { + std::vector< Sequence< Reference < browse::XBrowseNode > > > seqs; + seqs.reserve( m_Nodes.size() ); + + sal_Int32 numChildren = 0; + + for (Reference<XBrowseNode> & xNode : m_Nodes) + { + Sequence< Reference < browse::XBrowseNode > > children; + try + { + children = xNode->getChildNodes(); + seqs.push_back( children ); + numChildren += children.getLength(); + } + catch ( Exception& ) + { + // some form of exception getting child nodes so they + // won't be displayed + } + } + + Sequence< Reference < browse::XBrowseNode > > result( numChildren ); + sal_Int32 index = 0; + for ( const Sequence< Reference < browse::XBrowseNode > >& children : seqs ) + { + std::copy(children.begin(), children.end(), std::next(result.getArray(), index)); + index += children.getLength(); + + if (index >= numChildren) + break; + } + return result; + } + + virtual sal_Bool SAL_CALL + hasChildNodes() override + { + for (Reference<XBrowseNode> & xNode : m_Nodes) + { + try + { + if ( xNode->hasChildNodes() ) + { + return true; + } + } + catch ( Exception& ) + { + // some form of exception getting child nodes so move + // on to the next one + } + } + + return false; + } + + virtual sal_Int16 SAL_CALL getType() override + { + return browse::BrowseNodeTypes::CONTAINER; + } +}; + +struct alphaSort +{ + bool operator()( std::u16string_view a, std::u16string_view b ) + { + return a.compare( b ) < 0; + } +}; +class LocationBrowseNode : + public ::cppu::WeakImplHelper< browse::XBrowseNode > +{ +private: + std::optional<std::unordered_map< OUString, Reference< browse::XBrowseNode > >> m_hBNA; + std::vector< OUString > m_vStr; + OUString m_sNodeName; + Reference< browse::XBrowseNode > m_origNode; + +public: + + explicit LocationBrowseNode( const Reference< browse::XBrowseNode >& node ) + : m_sNodeName(node->getName()) + { + m_origNode.set( node ); + } + + + // XBrowseNode + + virtual OUString SAL_CALL getName() override + { + return m_sNodeName; + } + + virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL + getChildNodes() override + { + if ( !m_hBNA ) + { + loadChildNodes(); + } + + Sequence< Reference< browse::XBrowseNode > > children( m_hBNA->size() ); + auto childrenRange = asNonConstRange(children); + sal_Int32 index = 0; + + for ( const auto& str : m_vStr ) + { + childrenRange[ index ].set( m_hBNA->find( str )->second ); + ++index; + } + + return children; + } + + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return true; + } + + virtual sal_Int16 SAL_CALL getType() override + { + return browse::BrowseNodeTypes::CONTAINER; + } + +private: + + void loadChildNodes() + { + m_hBNA.emplace(); + + const Sequence< Reference< browse::XBrowseNode > > langNodes = + m_origNode->getChildNodes(); + + for ( const auto& rLangNode : langNodes ) + { + Reference< browse::XBrowseNode > xbn; + if ( rLangNode->getName() == "uno_packages" ) + { + xbn.set( new LocationBrowseNode( rLangNode ) ); + } + else + { + xbn.set( rLangNode ); + } + + const Sequence< Reference< browse::XBrowseNode > > grandchildren = + xbn->getChildNodes(); + + for ( const Reference< browse::XBrowseNode >& grandchild : grandchildren ) + { + auto h_it = + m_hBNA->find( grandchild->getName() ); + + if ( h_it != m_hBNA->end() ) + { + BrowseNodeAggregator* bna = static_cast< BrowseNodeAggregator* >( h_it->second.get() ); + bna->addBrowseNode( grandchild ); + } + else + { + Reference< browse::XBrowseNode > bna( + new BrowseNodeAggregator( grandchild ) ); + (*m_hBNA)[ grandchild->getName() ].set( bna ); + m_vStr.push_back( grandchild->getName() ); + } + } + } + // sort children alphabetically + ::std::sort( m_vStr.begin(), m_vStr.end(), alphaSort() ); + } +}; + +std::vector< Reference< browse::XBrowseNode > > getAllBrowseNodes( const Reference< XComponentContext >& xCtx ) +{ + const Sequence< OUString > openDocs = + MiscUtils::allOpenTDocUrls( xCtx ); + + Reference< provider::XScriptProviderFactory > xFac; + sal_Int32 initialSize = openDocs.getLength() + 2; + sal_Int32 mspIndex = 0; + + std::vector< Reference < browse::XBrowseNode > > locnBNs( initialSize ); + try + { + xFac = provider::theMasterScriptProviderFactory::get( xCtx ); + + locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( OUString("user") ) ), UNO_QUERY_THROW ); + locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( OUString("share") ) ), UNO_QUERY_THROW ); + } + // TODO proper exception handling, should throw + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("scripting", "Caught" ); + locnBNs.resize( mspIndex ); + return locnBNs; + } + + for ( const auto& rDoc : openDocs ) + { + try + { + Reference< frame::XModel > model( MiscUtils::tDocUrlToModel( rDoc ), UNO_SET_THROW ); + + // #i44599 Check if it's a real document or something special like Hidden/Preview + css::uno::Reference< css::frame::XController > xCurrentController = model->getCurrentController(); + if( xCurrentController.is() ) + { + utl::MediaDescriptor aMD( model->getArgs() ); + bool bDefault = false; + bool bHidden = aMD.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_HIDDEN, bDefault ); + bool bPreview = aMD.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_PREVIEW, bDefault ); + if( !bHidden && !bPreview ) + { + Reference< document::XEmbeddedScripts > xScripts( model, UNO_QUERY ); + if ( xScripts.is() ) + locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( model ) ), UNO_QUERY_THROW ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + + } + + std::vector< Reference < browse::XBrowseNode > > locnBNs_Return( mspIndex ); + for ( sal_Int32 j = 0; j < mspIndex; j++ ) + locnBNs_Return[j] = locnBNs[j]; + + return locnBNs_Return; +} + +} // namespace + +typedef ::std::vector< Reference< browse::XBrowseNode > > vXBrowseNodes; + +namespace { + +struct alphaSortForBNodes +{ + bool operator()( const Reference< browse::XBrowseNode >& a, const Reference< browse::XBrowseNode >& b ) + { + return a->getName().compareTo( b->getName() ) < 0; + } +}; + +} + +typedef ::cppu::WeakImplHelper< browse::XBrowseNode > t_BrowseNodeBase; + +namespace { + +class DefaultBrowseNode : + public t_BrowseNodeBase +{ + +private: + Reference< browse::XBrowseNode > m_xWrappedBrowseNode; + Reference< lang::XTypeProvider > m_xWrappedTypeProv; + Reference< XAggregation > m_xAggProxy; + Reference< XComponentContext > m_xCtx; + +public: + DefaultBrowseNode( const Reference< XComponentContext >& xCtx, const Reference< browse::XBrowseNode>& xNode ) : m_xWrappedBrowseNode( xNode ), m_xWrappedTypeProv( xNode, UNO_QUERY ), m_xCtx( xCtx ) + { + OSL_ENSURE( m_xWrappedBrowseNode.is(), "DefaultBrowseNode::DefaultBrowseNode(): No BrowseNode to wrap" ); + OSL_ENSURE( m_xWrappedTypeProv.is(), "DefaultBrowseNode::DefaultBrowseNode(): No BrowseNode to wrap" ); + OSL_ENSURE( m_xCtx.is(), "DefaultBrowseNode::DefaultBrowseNode(): No ComponentContext" ); + // Use proxy factory service to create aggregatable proxy. + try + { + Reference< reflection::XProxyFactory > xProxyFac = + reflection::ProxyFactory::create( m_xCtx ); + m_xAggProxy = xProxyFac->createProxy( m_xWrappedBrowseNode ); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "scripting", "DefaultBrowseNode::DefaultBrowseNode" ); + } + OSL_ENSURE( m_xAggProxy.is(), + "DefaultBrowseNode::DefaultBrowseNode: Wrapped BrowseNode cannot be aggregated!" ); + + if ( !m_xAggProxy.is() ) + return; + + osl_atomic_increment( &m_refCount ); + + /* i35609 - Fix crash on Solaris. The setDelegator call needs + to be in its own block to ensure that all temporary Reference + instances that are acquired during the call are released + before m_refCount is decremented again */ + { + m_xAggProxy->setDelegator( + static_cast< cppu::OWeakObject * >( this ) ); + } + + osl_atomic_decrement( &m_refCount ); + } + + virtual ~DefaultBrowseNode() override + { + if ( m_xAggProxy.is() ) + { + m_xAggProxy->setDelegator( uno::Reference< uno::XInterface >() ); + } + } + + virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL + getChildNodes() override + { + if ( hasChildNodes() ) + { + vXBrowseNodes aVNodes; + const Sequence < Reference< browse::XBrowseNode > > nodes = + m_xWrappedBrowseNode->getChildNodes(); + for ( const Reference< browse::XBrowseNode >& xBrowseNode : nodes ) + { + OSL_ENSURE( xBrowseNode.is(), "DefaultBrowseNode::getChildNodes(): Invalid BrowseNode" ); + if( xBrowseNode.is() ) + aVNodes.push_back( new DefaultBrowseNode( m_xCtx, xBrowseNode ) ); + } + + ::std::sort( aVNodes.begin(), aVNodes.end(), alphaSortForBNodes() ); + Sequence < Reference< browse::XBrowseNode > > children( aVNodes.size() ); + auto childrenRange = asNonConstRange(children); + sal_Int32 i = 0; + for ( const auto& rxNode : aVNodes ) + { + childrenRange[ i ].set( rxNode ); + i++; + } + return children; + } + else + { + // no nodes + + Sequence < Reference< browse::XBrowseNode > > none; + return none; + } + } + + virtual sal_Int16 SAL_CALL getType() override + { + return m_xWrappedBrowseNode->getType(); + } + + virtual OUString + SAL_CALL getName() override + { + return m_xWrappedBrowseNode->getName(); + } + + virtual sal_Bool SAL_CALL + hasChildNodes() override + { + return m_xWrappedBrowseNode->hasChildNodes(); + } + + // XInterface + virtual Any SAL_CALL queryInterface( const Type& aType ) override + { + Any aRet = t_BrowseNodeBase::queryInterface( aType ); + if ( aRet.hasValue() ) + { + return aRet; + } + if ( m_xAggProxy.is() ) + { + return m_xAggProxy->queryAggregation( aType ); + } + else + { + return Any(); + } + } + + // XTypeProvider (implemented by base, but needs to be overridden for + // delegating to aggregate) + virtual Sequence< Type > SAL_CALL getTypes() override + { + return m_xWrappedTypeProv->getTypes(); + } + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override + { + return css::uno::Sequence<sal_Int8>(); + } +}; + +class DefaultRootBrowseNode : + public ::cppu::WeakImplHelper< browse::XBrowseNode > +{ + +private: + vXBrowseNodes m_vNodes; + OUString m_Name; + +public: + explicit DefaultRootBrowseNode( const Reference< XComponentContext >& xCtx ) + { + std::vector< Reference< browse::XBrowseNode > > nodes = + getAllBrowseNodes( xCtx ); + + for (Reference< browse::XBrowseNode > & xNode : nodes) + { + m_vNodes.push_back( new DefaultBrowseNode( xCtx, xNode ) ); + } + m_Name = "Root"; + } + + virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL + getChildNodes() override + { + // no need to sort user, share, doc1...docN + //::std::sort( m_vNodes.begin(), m_vNodes.end(), alphaSortForBNodes() ); + Sequence < Reference< browse::XBrowseNode > > children( m_vNodes.size() ); + auto childrenRange = asNonConstRange(children); + sal_Int32 i = 0; + for ( const auto& rxNode : m_vNodes ) + { + childrenRange[ i ].set( rxNode ); + i++; + } + return children; + } + + virtual sal_Int16 SAL_CALL getType() override + { + return browse::BrowseNodeTypes::ROOT; + } + + virtual OUString + SAL_CALL getName() override + { + return m_Name; + } + + virtual sal_Bool SAL_CALL + hasChildNodes() override + { + bool result = true; + if ( m_vNodes.empty() ) + { + result = false; + } + return result; + } +}; + + +class SelectorBrowseNode : + public ::cppu::WeakImplHelper< browse::XBrowseNode > +{ +private: + Reference< XComponentContext > m_xComponentContext; + +public: + explicit SelectorBrowseNode( const Reference< XComponentContext >& xContext ) + : m_xComponentContext( xContext ) + { + } + + virtual OUString SAL_CALL getName() override + { + return "Root"; + } + + virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL + getChildNodes() override + { + + std::vector< Reference < browse::XBrowseNode > > locnBNs = getAllBrowseNodes( m_xComponentContext ); + + Sequence< Reference< browse::XBrowseNode > > children( + locnBNs.size() ); + auto childrenRange = asNonConstRange(children); + + for ( size_t j = 0; j < locnBNs.size(); j++ ) + { + childrenRange[j] = new LocationBrowseNode( locnBNs[j] ); + } + + return children; + } + + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return true; // will always be user and share + } + + virtual sal_Int16 SAL_CALL getType() override + { + return browse::BrowseNodeTypes::CONTAINER; + } +}; + +} + +BrowseNodeFactoryImpl::BrowseNodeFactoryImpl( + Reference< XComponentContext > const & xComponentContext ) + : m_xComponentContext( xComponentContext ) +{ +} + +BrowseNodeFactoryImpl::~BrowseNodeFactoryImpl() +{ +} + + +// Implementation of XBrowseNodeFactory + + +/* + * The selector hierarchy is the standard hierarchy for organizers with the + * language nodes removed. + */ +Reference< browse::XBrowseNode > SAL_CALL +BrowseNodeFactoryImpl::createView( sal_Int16 viewType ) +{ + switch( viewType ) + { + case browse::BrowseNodeFactoryViewTypes::MACROSELECTOR: + return new SelectorBrowseNode( m_xComponentContext ); + case browse::BrowseNodeFactoryViewTypes::MACROORGANIZER: + return getOrganizerHierarchy(); + default: + throw RuntimeException( "Unknown view type" ); + } +} + +Reference< browse::XBrowseNode > +BrowseNodeFactoryImpl::getOrganizerHierarchy() const +{ + Reference< browse::XBrowseNode > xRet = new DefaultRootBrowseNode( m_xComponentContext ); + return xRet; +} + +// Implementation of XServiceInfo + + +OUString SAL_CALL +BrowseNodeFactoryImpl::getImplementationName() +{ + return "com.sun.star.script.browse.BrowseNodeFactory"; +} + +Sequence< OUString > SAL_CALL +BrowseNodeFactoryImpl::getSupportedServiceNames() +{ + return { "com.sun.star.script.browse.BrowseNodeFactory" }; +} + +sal_Bool BrowseNodeFactoryImpl::supportsService(OUString const & serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_BrowseNodeFactoryImpl_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new BrowseNodeFactoryImpl(context)); +} + +} // namespace browsenodefactory + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/BrowseNodeFactoryImpl.hxx b/scripting/source/provider/BrowseNodeFactoryImpl.hxx new file mode 100644 index 000000000..908568021 --- /dev/null +++ b/scripting/source/provider/BrowseNodeFactoryImpl.hxx @@ -0,0 +1,70 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <com/sun/star/script/browse/XBrowseNode.hpp> +#include <com/sun/star/script/browse/XBrowseNodeFactory.hpp> + +namespace browsenodefactory +{ + +class BrowseNodeFactoryImpl : + public ::cppu::WeakImplHelper < + css::script::browse::XBrowseNodeFactory, + css::lang::XServiceInfo > +{ +private: + css::uno::Reference< css::uno::XComponentContext > m_xComponentContext; + +protected: + virtual ~BrowseNodeFactoryImpl() override; + +public: + explicit BrowseNodeFactoryImpl( + css::uno::Reference< css::uno::XComponentContext > const & xComponentContext ); + + // XServiceInfo + 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; + + // XBrowseNodeFactory + virtual css::uno::Reference< css::script::browse::XBrowseNode > SAL_CALL + createView( sal_Int16 viewType ) override; +private: + /// @throws css::uno::RuntimeException + css::uno::Reference< css::script::browse::XBrowseNode > + getOrganizerHierarchy() const; +}; + + +} // namespace browsenodefactory + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/MasterScriptProvider.cxx b/scripting/source/provider/MasterScriptProvider.cxx new file mode 100644 index 000000000..93d6a60f3 --- /dev/null +++ b/scripting/source/provider/MasterScriptProvider.cxx @@ -0,0 +1,676 @@ +/* -*- 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/SetFlagContextHelper.hxx> +#include <comphelper/documentinfo.hxx> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> + +#include <com/sun/star/deployment/XPackage.hpp> +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp> + +#include <util/MiscUtils.hxx> +#include <sal/log.hxx> + +#include "MasterScriptProvider.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::document; +using namespace ::sf_misc; + +namespace func_provider +{ + +static bool endsWith( const OUString& target, const OUString& item ) +{ + sal_Int32 index = target.indexOf( item ); + return index != -1 && + index == ( target.getLength() - item.getLength() ); +} + +/* should be available in some central location. */ + +// XScriptProvider implementation + + +MasterScriptProvider::MasterScriptProvider( const Reference< XComponentContext > & xContext ): + m_xContext( xContext ), m_bIsValid( false ), m_bInitialised( false ), + m_bIsPkgMSP( false ) +{ + ENSURE_OR_THROW( m_xContext.is(), "MasterScriptProvider::MasterScriptProvider: No context available\n" ); + m_xMgr = m_xContext->getServiceManager(); + ENSURE_OR_THROW( m_xMgr.is(), "MasterScriptProvider::MasterScriptProvider: No service manager available\n" ); + m_bIsValid = true; +} + + +MasterScriptProvider::~MasterScriptProvider() +{ +} + + +void SAL_CALL MasterScriptProvider::initialize( const Sequence < Any >& args ) +{ + if ( m_bInitialised ) + return; + + m_bIsValid = false; + + sal_Int32 len = args.getLength(); + if ( len > 1 ) + { + throw RuntimeException( + "MasterScriptProvider::initialize: invalid number of arguments" ); + } + + Sequence< Any > invokeArgs( len ); + + if ( len != 0 ) + { + auto pinvokeArgs = invokeArgs.getArray(); + // check if first parameter is a string + // if it is, this implies that this is a MSP created + // with a user or share ctx ( used for browse functionality ) + + if ( args[ 0 ] >>= m_sCtxString ) + { + pinvokeArgs[ 0 ] = args[ 0 ]; + if ( m_sCtxString.startsWith( "vnd.sun.star.tdoc" ) ) + { + m_xModel = MiscUtils::tDocUrlToModel( m_sCtxString ); + } + } + else if ( args[ 0 ] >>= m_xInvocationContext ) + { + m_xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY_THROW ); + } + else + { + args[ 0 ] >>= m_xModel; + } + + if ( m_xModel.is() ) + { + // from the arguments, we were able to deduce a model. That alone doesn't + // suffice, we also need an XEmbeddedScripts which actually indicates support + // for embedding scripts + Reference< XEmbeddedScripts > xScripts( m_xModel, UNO_QUERY ); + if ( !xScripts.is() ) + { + throw lang::IllegalArgumentException( + "The given document does not support embedding scripts into it, and cannot be associated with such a document.", + *this, + 1 + ); + } + + try + { + m_sCtxString = MiscUtils::xModelToTdocUrl( m_xModel, m_xContext ); + } + catch ( const Exception& ) + { + Any aError( ::cppu::getCaughtException() ); + + Exception aException; + aError >>= aException; + OUString buf = + "MasterScriptProvider::initialize: caught " + + aError.getValueTypeName() + + ":" + + aException.Message; + throw lang::WrappedTargetException( buf, *this, aError ); + } + + if ( m_xInvocationContext.is() && m_xInvocationContext != m_xModel ) + pinvokeArgs[ 0 ] <<= m_xInvocationContext; + else + pinvokeArgs[ 0 ] <<= m_sCtxString; + } + + OUString pkgSpec = "uno_packages"; + sal_Int32 indexOfPkgSpec = m_sCtxString.lastIndexOf( pkgSpec ); + + // if context string ends with "uno_packages" + if ( indexOfPkgSpec > -1 && m_sCtxString.match( pkgSpec, indexOfPkgSpec ) ) + { + m_bIsPkgMSP = true; + } + else + { + m_bIsPkgMSP = false; + } + } + else // no args + { + // use either scripting context or maybe zero args? + invokeArgs = Sequence< Any >( 0 ); // no arguments + } + m_sAargs = invokeArgs; + // don't create pkg mgr MSP for documents, not supported + if ( !m_bIsPkgMSP && !m_xModel.is() ) + { + createPkgProvider(); + } + + m_bInitialised = true; + m_bIsValid = true; +} + + +void MasterScriptProvider::createPkgProvider() +{ + try + { + Any location; + location <<= m_sCtxString + ":uno_packages"; + + Reference< provider::XScriptProviderFactory > xFac = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + m_xMSPPkg.set( + xFac->createScriptProvider( location ), UNO_SET_THROW ); + + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION("scripting.provider", "Exception creating MasterScriptProvider for uno_packages in context " + << m_sCtxString ); + } +} + + +Reference< provider::XScript > +MasterScriptProvider::getScript( const OUString& scriptURI ) +{ + if ( !m_bIsValid ) + { + throw provider::ScriptFrameworkErrorException( + "MasterScriptProvider not initialised", Reference< XInterface >(), + scriptURI, "", + provider::ScriptFrameworkErrorType::UNKNOWN ); + } + + // need to get the language from the string + + Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) ); + + Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI ); + + Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY ); + + if ( !uriRef.is() || !sfUri.is() ) + { + throw provider::ScriptFrameworkErrorException( + "Incorrect format for Script URI: " + scriptURI, + Reference< XInterface >(), + scriptURI, "", + provider::ScriptFrameworkErrorType::UNKNOWN ); + } + + OUString langKey("language"); + OUString locKey("location"); + + if ( !sfUri->hasParameter( langKey ) || + !sfUri->hasParameter( locKey ) || + ( sfUri->getName().isEmpty() ) ) + { + throw provider::ScriptFrameworkErrorException( + "Incorrect format for Script URI: " + scriptURI, + Reference< XInterface >(), + scriptURI, "", + provider::ScriptFrameworkErrorType::UNKNOWN ); + } + + OUString language = sfUri->getParameter( langKey ); + OUString location = sfUri->getParameter( locKey ); + + // if script us located in uno pkg + sal_Int32 index = -1; + OUString pkgTag(":uno_packages"); + // for languages other than basic, scripts located in uno packages + // are merged into the user/share location context. + // For other languages the location attribute in script url has the form + // location = [user|share]:uno_packages or location :uno_packages/xxxx.uno.pkg + // we need to extract the value of location part from the + // location attribute of the script, if the script is located in an + // uno package then that is the location part up to and including + // ":uno_packages", if the script is not in a uno package then the + // normal value is used e.g. user or share. + // The value extracted will be used to determine if the script is + // located in the same location context as this MSP. + // For Basic, the language script provider can handle the execution of a + // script in any location context + if ( ( index = location.indexOf( pkgTag ) ) > -1 ) + { + location = location.copy( 0, index + pkgTag.getLength() ); + } + + Reference< provider::XScript > xScript; + + // If the script location is in the same location context as this + // MSP then delete to the language provider controlled by this MSP + // ** Special case is BASIC, all calls to getScript will be handled + // by the language script provider in the current location context + // even if it's different + if ( ( location == "document" + && m_xModel.is() + ) + || ( endsWith( m_sCtxString, location ) ) + || ( language == "Basic" ) + ) + { + Reference< provider::XScriptProvider > xScriptProvider; + OUString serviceName = "com.sun.star.script.provider.ScriptProviderFor" + language; + if ( !providerCache() ) + { + throw provider::ScriptFrameworkErrorException( + "No LanguageProviders detected", + Reference< XInterface >(), + sfUri->getName(), language, + provider::ScriptFrameworkErrorType::NOTSUPPORTED ); + } + + try + { + xScriptProvider.set( + providerCache()->getProvider( serviceName ), + UNO_SET_THROW ); + } + catch( const Exception& e ) + { + throw provider::ScriptFrameworkErrorException( + e.Message, Reference< XInterface >(), + sfUri->getName(), language, + provider::ScriptFrameworkErrorType::NOTSUPPORTED ); + } + + xScript=xScriptProvider->getScript( scriptURI ); + } + else + { + Reference< provider::XScriptProviderFactory > xFac_ = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + Reference< provider::XScriptProvider > xSP( + xFac_->createScriptProvider( Any( location ) ), UNO_SET_THROW ); + xScript = xSP->getScript( scriptURI ); + } + + return xScript; +} + + +ProviderCache* +MasterScriptProvider::providerCache() +{ + if ( !m_pPCache ) + { + std::scoped_lock aGuard( m_mutex ); + if ( !m_pPCache ) + { + Sequence<OUString> denylist { "com.sun.star.script.provider.ScriptProviderForBasic" }; + + if ( !m_bIsPkgMSP ) + { + m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs ) ); + } + else + { + m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs, denylist ) ); + } + } + } + return m_pPCache.get(); +} + + +OUString SAL_CALL +MasterScriptProvider::getName() +{ + if ( !m_bIsPkgMSP ) + { + OUString sCtx = getContextString(); + if ( sCtx.startsWith( "vnd.sun.star.tdoc" ) ) + { + Reference< frame::XModel > xModel = m_xModel; + if ( !xModel.is() ) + { + xModel = MiscUtils::tDocUrlToModel( sCtx ); + } + + m_sNodeName = ::comphelper::DocumentInfo::getDocumentTitle( xModel ); + } + else + { + m_sNodeName = parseLocationName( getContextString() ); + } + } + else + { + m_sNodeName = "uno_packages"; + } + return m_sNodeName; +} + + +Sequence< Reference< browse::XBrowseNode > > SAL_CALL +MasterScriptProvider::getChildNodes() +{ + Sequence< Reference< provider::XScriptProvider > > providers = providerCache()->getAllProviders(); + + sal_Int32 size = providers.getLength(); + bool hasPkgs = m_xMSPPkg.is(); + if ( hasPkgs ) + { + size++; + } + Sequence< Reference< browse::XBrowseNode > > children( size ); + auto childrenRange = asNonConstRange(children); + sal_Int32 provIndex = 0; + for ( ; provIndex < providers.getLength(); provIndex++ ) + { + childrenRange[ provIndex ].set( providers[ provIndex ], UNO_QUERY ); + } + + if ( hasPkgs ) + { + childrenRange[ provIndex ].set( m_xMSPPkg, UNO_QUERY ); + + } + + return children; +} + + +sal_Bool SAL_CALL +MasterScriptProvider::hasChildNodes() +{ + return true; +} + + +sal_Int16 SAL_CALL +MasterScriptProvider::getType() +{ + return browse::BrowseNodeTypes::CONTAINER; +} + + +OUString +MasterScriptProvider::parseLocationName( const OUString& location ) +{ + // strip out the last leaf of location name + // e.g. file://dir1/dir2/Blah.sxw - > Blah.sxw + OUString temp = location; + INetURLObject aURLObj( temp ); + if ( !aURLObj.HasError() ) + temp = aURLObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + return temp; +} + +namespace +{ +template <typename Proc> bool FindProviderAndApply(ProviderCache& rCache, Proc p) +{ + auto pass = [&rCache, &p]() -> bool + { + bool bResult = false; + const Sequence<Reference<provider::XScriptProvider>> aAllProviders = rCache.getAllProviders(); + for (const auto& rProv : aAllProviders) + { + Reference<container::XNameContainer> xCont(rProv, UNO_QUERY); + if (!xCont.is()) + { + continue; + } + try + { + bResult = p(xCont); + if (bResult) + break; + } + catch (const Exception&) + { + TOOLS_INFO_EXCEPTION("scripting.provider", "ignoring"); + } + } + return bResult; + }; + bool bSuccess = false; + // 1. Try to perform the operation without trying to enable JVM (if disabled) + // This allows us to avoid useless user interaction in case when other provider + // (not JVM) actually handles the operation. + { + css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext()); + bSuccess = pass(); + } + // 2. Now retry asking to enable JVM in case we didn't succeed first time + if (!bSuccess) + { + bSuccess = pass(); + } + return bSuccess; +} +} // namespace + +// Register Package +void SAL_CALL +MasterScriptProvider::insertByName( const OUString& aName, const Any& aElement ) +{ + if ( !m_bIsPkgMSP ) + { + if ( !m_xMSPPkg.is() ) + { + throw RuntimeException( "PackageMasterScriptProvider is unitialised" ); + } + + Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW ); + xCont->insertByName( aName, aElement ); + } + else + { + Reference< deployment::XPackage > xPkg( aElement, UNO_QUERY ); + if ( !xPkg.is() ) + { + throw lang::IllegalArgumentException( "Couldn't convert to XPackage", + Reference < XInterface > (), 2 ); + } + if ( aName.isEmpty() ) + { + throw lang::IllegalArgumentException( "Name not set!!", + Reference < XInterface > (), 1 ); + } + // TODO for library package parse the language, for the moment will try + // to get each provider to process the new Package, the first one the succeeds + // will terminate processing + const bool bSuccess = FindProviderAndApply( + *providerCache(), [&aName, &aElement](Reference<container::XNameContainer>& xCont) { + xCont->insertByName(aName, aElement); + return true; + }); + if (!bSuccess) + { + // No script providers could process the package + throw lang::IllegalArgumentException( "Failed to register package for " + aName, + Reference < XInterface > (), 2 ); + } + } +} + + +// Revoke Package +void SAL_CALL +MasterScriptProvider::removeByName( const OUString& Name ) +{ + if ( !m_bIsPkgMSP ) + { + if ( !m_xMSPPkg.is() ) + { + throw RuntimeException( "PackageMasterScriptProvider is unitialised" ); + } + + Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW ); + xCont->removeByName( Name ); + } + else + { + if ( Name.isEmpty() ) + { + throw lang::IllegalArgumentException( "Name not set!!", + Reference < XInterface > (), 1 ); + } + // TODO for Script library package url parse the language, + // for the moment will just try to get each provider to process remove/revoke + // request, the first one the succeeds will terminate processing + const bool bSuccess = FindProviderAndApply( + *providerCache(), [&Name](Reference<container::XNameContainer>& xCont) { + xCont->removeByName(Name); + return true; + }); + if (!bSuccess) + { + // No script providers could process the package + throw lang::IllegalArgumentException( "Failed to revoke package for " + Name, + Reference < XInterface > (), 1 ); + } + + } +} + + +void SAL_CALL +MasterScriptProvider::replaceByName( const OUString& /*aName*/, const Any& /*aElement*/ ) +{ + // TODO needs implementing + throw RuntimeException( "replaceByName not implemented!!!!" ); +} + +Any SAL_CALL +MasterScriptProvider::getByName( const OUString& /*aName*/ ) +{ + // TODO needs to be implemented + throw RuntimeException( "getByName not implemented!!!!" ); +} + +sal_Bool SAL_CALL +MasterScriptProvider::hasByName( const OUString& aName ) +{ + bool result = false; + if ( !m_bIsPkgMSP ) + { + if ( m_xMSPPkg.is() ) + { + Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW ); + result = xCont->hasByName( aName ); + } + // If this is a document provider then we shouldn't + // have a PackageProvider + else if (!m_xModel.is()) + { + throw RuntimeException( "PackageMasterScriptProvider is unitialised" ); + } + + } + else + { + if ( aName.isEmpty() ) + { + throw lang::IllegalArgumentException( "Name not set!!", + Reference < XInterface > (), 1 ); + } + // TODO for Script library package url parse the language, + // for the moment will just try to get each provider to see if the + // package exists in any provider, first one that succeed will + // terminate the loop + result = FindProviderAndApply( + *providerCache(), [&aName](Reference<container::XNameContainer>& xCont) { + return xCont->hasByName(aName); + }); + } + return result; +} + + +Sequence< OUString > SAL_CALL +MasterScriptProvider::getElementNames( ) +{ + // TODO needs implementing + throw RuntimeException( "getElementNames not implemented!!!!" ); +} + +Type SAL_CALL +MasterScriptProvider::getElementType( ) +{ + // TODO needs implementing + Type t; + return t; +} + +sal_Bool SAL_CALL MasterScriptProvider::hasElements( ) +{ + // TODO needs implementing + throw RuntimeException( "hasElements not implemented!!!!" ); +} + + +OUString SAL_CALL MasterScriptProvider::getImplementationName( ) +{ + return "com.sun.star.script.provider.MasterScriptProvider"; +} + +sal_Bool SAL_CALL MasterScriptProvider::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + + +Sequence< OUString > SAL_CALL MasterScriptProvider::getSupportedServiceNames( ) +{ + return { + "com.sun.star.script.provider.MasterScriptProvider", + "com.sun.star.script.browse.BrowseNode", + "com.sun.star.script.provider.ScriptProvider" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_MasterScriptProvider_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new MasterScriptProvider(context)); +} + +} // namespace func_provider + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/MasterScriptProvider.hxx b/scripting/source/provider/MasterScriptProvider.hxx new file mode 100644 index 000000000..0e6c40f5f --- /dev/null +++ b/scripting/source/provider/MasterScriptProvider.hxx @@ -0,0 +1,131 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> + +#include <com/sun/star/lang/XInitialization.hpp> + +#include <com/sun/star/script/provider/XScriptProvider.hpp> +#include <com/sun/star/script/browse/XBrowseNode.hpp> + +#include "ProviderCache.hxx" +#include <memory> +#include <mutex> + +namespace func_provider +{ + + typedef ::cppu::WeakImplHelper< + css::script::provider::XScriptProvider, + css::script::browse::XBrowseNode, css::lang::XServiceInfo, + css::lang::XInitialization, + css::container::XNameContainer > t_helper; + +class MasterScriptProvider : + public t_helper +{ +public: + /// @throws css::uno::RuntimeException + explicit MasterScriptProvider( + const css::uno::Reference< css::uno::XComponentContext > + & xContext ); + virtual ~MasterScriptProvider() override; + + // XServiceInfo implementation + virtual OUString SAL_CALL getImplementationName( ) override; + + // XBrowseNode implementation + virtual OUString SAL_CALL getName() override; + virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes() override; + virtual sal_Bool SAL_CALL hasChildNodes() override; + virtual sal_Int16 SAL_CALL getType() override; + // XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XScriptProvider implementation + virtual css::uno::Reference < css::script::provider::XScript > SAL_CALL + getScript( const OUString& scriptURI ) override; + + /** + * XInitialise implementation + * + * @param args expected to contain a single OUString + * containing the URI + */ + virtual void SAL_CALL initialize( const css::uno::Sequence < css::uno::Any > & args ) override; + + // returns context string for this provider, eg + const OUString& getContextString() const { return m_sCtxString; } + +private: + static OUString parseLocationName( const OUString& location ); + void createPkgProvider(); + + ProviderCache* providerCache(); + /* to obtain other services if needed */ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::lang::XMultiComponentFactory > m_xMgr; + css::uno::Reference< css::frame::XModel > m_xModel; + css::uno::Reference< css::document::XScriptInvocationContext > m_xInvocationContext; + css::uno::Sequence< css::uno::Any > m_sAargs; + OUString m_sNodeName; + + // This component supports XInitialization, it can be created + // using createInstanceXXX() or createInstanceWithArgumentsXXX using + // the service Manager. + // Need to detect proper initialisation and validity + // for the object, so m_bIsValid indicates that the object is valid is set in ctor + // in case of createInstanceWithArgumentsXXX() called m_bIsValid is set to reset + // and then set to true when initialisation is complete + bool m_bIsValid; + // m_bInitialised ensure initialisation only takes place once. + bool m_bInitialised; + bool m_bIsPkgMSP; + css::uno::Reference< css::script::provider::XScriptProvider > m_xMSPPkg; + std::unique_ptr<ProviderCache> m_pPCache; + std::mutex m_mutex; + OUString m_sCtxString; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/MasterScriptProviderFactory.cxx b/scripting/source/provider/MasterScriptProviderFactory.cxx new file mode 100644 index 000000000..acdb8f8da --- /dev/null +++ b/scripting/source/provider/MasterScriptProviderFactory.cxx @@ -0,0 +1,85 @@ +/* -*- 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/supportsservice.hxx> + +#include "MasterScriptProviderFactory.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; + +namespace func_provider +{ + +MasterScriptProviderFactory::MasterScriptProviderFactory( + Reference< XComponentContext > const & xComponentContext ) + : m_xComponentContext( xComponentContext ) +{ +} + +MasterScriptProviderFactory::~MasterScriptProviderFactory() +{ +} + +Reference< provider::XScriptProvider > SAL_CALL +MasterScriptProviderFactory::createScriptProvider( const Any& context ) +{ + Reference< provider::XScriptProvider > xMsp( getActiveMSPList() ->getMSPFromAnyContext( context ), UNO_SET_THROW ); + return xMsp; +} + +const rtl::Reference< ActiveMSPList > & +MasterScriptProviderFactory::getActiveMSPList() const +{ + if ( !m_MSPList.is() ) + { + ::osl::MutexGuard guard( ::osl::Mutex::getGlobalMutex() ); + if ( !m_MSPList.is() ) + m_MSPList = new ActiveMSPList( m_xComponentContext ); + } + return m_MSPList; +} + +OUString SAL_CALL MasterScriptProviderFactory::getImplementationName() +{ + return "com.sun.star.script.provider.MasterScriptProviderFactory"; +} + +Sequence< OUString > SAL_CALL MasterScriptProviderFactory::getSupportedServiceNames() +{ + return { "com.sun.star.script.provider.MasterScriptProviderFactory" }; +} + +sal_Bool MasterScriptProviderFactory::supportsService( + OUString const & serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +} // namespace func_provider + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_MasterScriptProviderFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new func_provider::MasterScriptProviderFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/MasterScriptProviderFactory.hxx b/scripting/source/provider/MasterScriptProviderFactory.hxx new file mode 100644 index 000000000..9fbc8705d --- /dev/null +++ b/scripting/source/provider/MasterScriptProviderFactory.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <rtl/ref.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <com/sun/star/script/provider/XScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/XScriptProvider.hpp> + +#include "ActiveMSPList.hxx" + +namespace func_provider +{ + +class MasterScriptProviderFactory : + public ::cppu::WeakImplHelper < + css::script::provider::XScriptProviderFactory, + css::lang::XServiceInfo > +{ +private: + + mutable rtl::Reference< ActiveMSPList > m_MSPList; + + const css::uno::Reference< css::uno::XComponentContext > m_xComponentContext; + + const rtl::Reference< ActiveMSPList > & getActiveMSPList() const; + +protected: + virtual ~MasterScriptProviderFactory() override; + +public: + explicit MasterScriptProviderFactory( + css::uno::Reference< css::uno::XComponentContext > const & xComponentContext ); + + // XServiceInfo + 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; + + // XScriptProviderFactory + virtual css::uno::Reference< css::script::provider::XScriptProvider > + SAL_CALL createScriptProvider( const css::uno::Any& context ) override; +}; + + +} // namespace func_provider + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/ProviderCache.cxx b/scripting/source/provider/ProviderCache.cxx new file mode 100644 index 000000000..8533fc384 --- /dev/null +++ b/scripting/source/provider/ProviderCache.cxx @@ -0,0 +1,203 @@ +/* -*- 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 <tools/diagnose_ex.h> +#include <sal/log.hxx> + +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include "ProviderCache.hxx" + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::script; + +namespace func_provider +{ + +ProviderCache::ProviderCache( const Reference< XComponentContext >& xContext, const Sequence< Any >& scriptContext ) : m_Sctx( scriptContext ), m_xContext( xContext ) +{ + // initialise m_hProviderDetailsCache with details of ScriptProviders + // will use createContentEnumeration + + m_xMgr = m_xContext->getServiceManager(); + ENSURE_OR_THROW( m_xMgr.is(), "ProviderCache::ProviderCache() failed to obtain ServiceManager" ); + populateCache(); +} + + +ProviderCache::ProviderCache( const Reference< XComponentContext >& xContext, const Sequence< Any >& scriptContext, const Sequence< OUString >& denyList ) : m_sDenyList( denyList ), m_Sctx( scriptContext ), m_xContext( xContext ) + +{ + // initialise m_hProviderDetailsCache with details of ScriptProviders + // will use createContentEnumeration + + m_xMgr = m_xContext->getServiceManager(); + ENSURE_OR_THROW( m_xMgr.is(), "ProviderCache::ProviderCache() failed to obtain ServiceManager" ); + populateCache(); +} + +ProviderCache::~ProviderCache() +{ +} + +Reference< provider::XScriptProvider > +ProviderCache::getProvider( const OUString& providerName ) +{ + std::scoped_lock aGuard( m_mutex ); + Reference< provider::XScriptProvider > provider; + ProviderDetails_hash::iterator h_it = m_hProviderDetailsCache.find( providerName ); + if ( h_it != m_hProviderDetailsCache.end() ) + { + if ( h_it->second.provider.is() ) + { + provider = h_it->second.provider; + } + else + { + // need to create provider and insert into hash + provider = createProvider( h_it->second ); + } + } + return provider; +} + +Sequence < Reference< provider::XScriptProvider > > +ProviderCache::getAllProviders() +{ + // need to create providers that haven't been created already + // so check what providers exist and what ones don't + + std::scoped_lock aGuard( m_mutex ); + Sequence < Reference< provider::XScriptProvider > > providers ( m_hProviderDetailsCache.size() ); + // should assert if size !> 0 + if ( !m_hProviderDetailsCache.empty() ) + { + auto pproviders = providers.getArray(); + sal_Int32 providerIndex = 0; + for (auto& rDetail : m_hProviderDetailsCache) + { + Reference<provider::XScriptProvider> xScriptProvider = rDetail.second.provider; + if ( xScriptProvider.is() ) + { + pproviders[ providerIndex++ ] = xScriptProvider; + } + else + { + // create provider + try + { + xScriptProvider = createProvider(rDetail.second); + pproviders[ providerIndex++ ] = xScriptProvider; + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("scripting"); + } + } + } + + if (providerIndex < providers.getLength()) + { + providers.realloc( providerIndex ); + } + + } + else + { + SAL_WARN("scripting", "no available providers, something very wrong!!!"); + } + return providers; +} + +void +ProviderCache::populateCache() +{ + // wrong name in services.rdb + OUString serviceName; + std::scoped_lock aGuard( m_mutex ); + try + { + Reference< container::XContentEnumerationAccess > xEnumAccess( m_xMgr, UNO_QUERY_THROW ); + Reference< container::XEnumeration > xEnum = xEnumAccess->createContentEnumeration ( "com.sun.star.script.provider.LanguageScriptProvider" ); + + while ( xEnum->hasMoreElements() ) + { + + Reference< lang::XSingleComponentFactory > factory( xEnum->nextElement(), UNO_QUERY_THROW ); + Reference< lang::XServiceInfo > xServiceInfo( factory, UNO_QUERY_THROW ); + + const Sequence< OUString > serviceNames = xServiceInfo->getSupportedServiceNames(); + + if ( serviceNames.hasElements() ) + { + auto pName = std::find_if(serviceNames.begin(), serviceNames.end(), + [this](const OUString& rName) { + return rName.startsWith("com.sun.star.script.provider.ScriptProviderFor") + && !isInDenyList(rName); + }); + if (pName != serviceNames.end()) + { + serviceName = *pName; + ProviderDetails details; + details.factory = factory; + m_hProviderDetailsCache[ serviceName ] = details; + } + } + } + } + catch ( const Exception &e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "ProviderCache::populateCache: couldn't obtain XSingleComponentFactory for " + serviceName + + " " + e.Message, + nullptr, anyEx ); + } +} + +Reference< provider::XScriptProvider > +ProviderCache::createProvider( ProviderDetails& details ) +{ + try + { + details.provider.set( + details.factory->createInstanceWithArgumentsAndContext( m_Sctx, m_xContext ), UNO_QUERY_THROW ); + } + catch ( const Exception& e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "ProviderCache::createProvider() Error creating provider from factory. " + e.Message, + nullptr, anyEx ); + } + + return details.provider; +} + +bool +ProviderCache::isInDenyList( const OUString& serviceName ) const +{ + return comphelper::findValue(m_sDenyList, serviceName) != -1; +} +} //end namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/ProviderCache.hxx b/scripting/source/provider/ProviderCache.hxx new file mode 100644 index 000000000..b565d1d08 --- /dev/null +++ b/scripting/source/provider/ProviderCache.hxx @@ -0,0 +1,80 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> + +#include <com/sun/star/script/provider/XScriptProvider.hpp> + +#include <mutex> +#include <unordered_map> + +namespace func_provider +{ + +//Typedefs + + +struct ProviderDetails +{ + //css::uno::Reference< css::lang::XSingleServiceFactory > factory; + css::uno::Reference< css::lang::XSingleComponentFactory > factory; + css::uno::Reference< css::script::provider::XScriptProvider > provider; +}; +typedef std::unordered_map < OUString, ProviderDetails > ProviderDetails_hash; + + +class ProviderCache +{ + +public: + /// @throws css::uno::RuntimeException + ProviderCache( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Sequence< css::uno::Any >& scriptContext ); + /// @throws css::uno::RuntimeException + ProviderCache( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Sequence< css::uno::Any >& scriptContext, + const css::uno::Sequence< OUString >& denyList ); + ~ProviderCache(); + css::uno::Reference< css::script::provider::XScriptProvider > + getProvider( const OUString& providerName ); + /// @throws css::uno::RuntimeException + css::uno::Sequence < css::uno::Reference< css::script::provider::XScriptProvider > > + getAllProviders(); +private: + /// @throws css::uno::RuntimeException + void populateCache(); + + /// @throws css::uno::RuntimeException + css::uno::Reference< css::script::provider::XScriptProvider > + createProvider( ProviderDetails& details ); + bool isInDenyList( const OUString& serviceName ) const; + css::uno::Sequence< OUString > m_sDenyList; + ProviderDetails_hash m_hProviderDetailsCache; + std::mutex m_mutex; + css::uno::Sequence< css::uno::Any > m_Sctx; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Reference< css::lang::XMultiComponentFactory > m_xMgr; + + +}; +} // func_provider + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/URIHelper.cxx b/scripting/source/provider/URIHelper.cxx new file mode 100644 index 000000000..5333bee34 --- /dev/null +++ b/scripting/source/provider/URIHelper.cxx @@ -0,0 +1,256 @@ +/* -*- 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 <config_folders.h> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> +#include "URIHelper.hxx" + +namespace func_provider +{ + +namespace uno = ::com::sun::star::uno; +namespace ucb = ::com::sun::star::ucb; +namespace lang = ::com::sun::star::lang; +namespace uri = ::com::sun::star::uri; + +constexpr OUStringLiteral SHARE = u"share"; + +constexpr OUStringLiteral SHARE_UNO_PACKAGES_URI = + u"vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE"; + +constexpr OUStringLiteral USER = u"user"; +constexpr OUStringLiteral USER_URI = + u"vnd.sun.star.expand:${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}"; + + + +ScriptingFrameworkURIHelper::ScriptingFrameworkURIHelper( + const uno::Reference< uno::XComponentContext >& xContext) +{ + try + { + m_xSimpleFileAccess = ucb::SimpleFileAccess::create(xContext); + } + catch (uno::Exception&) + { + OSL_FAIL("Scripting Framework error initialising XSimpleFileAccess"); + } + + try + { + m_xUriReferenceFactory = uri::UriReferenceFactory::create( xContext ); + } + catch (uno::Exception&) + { + OSL_FAIL("Scripting Framework error initialising XUriReferenceFactory"); + } +} + +ScriptingFrameworkURIHelper::~ScriptingFrameworkURIHelper() +{ + // currently does nothing +} + +void SAL_CALL +ScriptingFrameworkURIHelper::initialize( + const uno::Sequence < uno::Any >& args ) +{ + if ( args.getLength() != 2 || + args[0].getValueType() != ::cppu::UnoType<OUString>::get() || + args[1].getValueType() != ::cppu::UnoType<OUString>::get() ) + { + throw uno::RuntimeException( "ScriptingFrameworkURIHelper got invalid argument list" ); + } + + if ( !(args[0] >>= m_sLanguage) || !(args[1] >>= m_sLocation) ) + { + throw uno::RuntimeException( "ScriptingFrameworkURIHelper error parsing args" ); + } + + SCRIPTS_PART = "/Scripts/" + m_sLanguage.toAsciiLowerCase(); + + if ( !initBaseURI() ) + { + throw uno::RuntimeException( "ScriptingFrameworkURIHelper cannot find script directory" ); + } +} + +bool +ScriptingFrameworkURIHelper::initBaseURI() +{ + OUString uri, test; + bool bAppendScriptsPart = false; + + if ( m_sLocation == USER ) + { + test = USER; + uri = USER_URI; + bAppendScriptsPart = true; + } + else if ( m_sLocation == "user:uno_packages" ) + { + test = "uno_packages"; + uri = USER_URI + "/user/uno_packages/cache"; + } + else if (m_sLocation == SHARE) + { + test = SHARE; + uri = "vnd.sun.star.expand:$BRAND_BASE_DIR"; + bAppendScriptsPart = true; + } + else if (m_sLocation == "share:uno_packages") + { + test = "uno_packages"; + uri = SHARE_UNO_PACKAGES_URI; + } + else if (m_sLocation.startsWith("vnd.sun.star.tdoc")) + { + m_sBaseURI = m_sLocation + SCRIPTS_PART; + m_sLocation = "document"; + return true; + } + else + { + return false; + } + + if ( !m_xSimpleFileAccess->exists( uri ) || + !m_xSimpleFileAccess->isFolder( uri ) ) + { + return false; + } + + const uno::Sequence< OUString > children = + m_xSimpleFileAccess->getFolderContents( uri, true ); + + auto pChild = std::find_if(children.begin(), children.end(), [&test](const OUString& child) { + sal_Int32 idx = child.lastIndexOf(test); + return idx != -1 && (idx + test.getLength()) == child.getLength(); + }); + if (pChild != children.end()) + { + if ( bAppendScriptsPart ) + { + m_sBaseURI = *pChild + SCRIPTS_PART; + } + else + { + m_sBaseURI = *pChild; + } + return true; + } + return false; +} + +OUString +ScriptingFrameworkURIHelper::getLanguagePart(std::u16string_view rStorageURI) +{ + OUString result; + + size_t idx = rStorageURI.find(m_sBaseURI); + sal_Int32 len = m_sBaseURI.getLength() + 1; + + if ( idx != std::u16string_view::npos ) + { + result = rStorageURI.substr(idx + len); + result = result.replace('/', '|'); + } + return result; +} + +OUString +ScriptingFrameworkURIHelper::getLanguagePath(const OUString& rLanguagePart) +{ + OUString result = rLanguagePart.replace('|', '/'); + return result; +} + +OUString SAL_CALL +ScriptingFrameworkURIHelper::getScriptURI(const OUString& rStorageURI) +{ + return + "vnd.sun.star.script:" + + getLanguagePart(rStorageURI) + + "?language=" + + m_sLanguage + + "&location=" + + m_sLocation; +} + +OUString SAL_CALL +ScriptingFrameworkURIHelper::getStorageURI(const OUString& rScriptURI) +{ + OUString sLanguagePart; + try + { + uno::Reference < uri::XVndSunStarScriptUrl > xURI( + m_xUriReferenceFactory->parse( rScriptURI ), uno::UNO_QUERY_THROW ); + sLanguagePart = xURI->getName(); + } + catch ( uno::Exception& ) + { + throw lang::IllegalArgumentException( + "Script URI not valid", + uno::Reference< uno::XInterface >(), 1 ); + } + + return m_sBaseURI + "/" + getLanguagePath(sLanguagePart); +} + +OUString SAL_CALL +ScriptingFrameworkURIHelper::getRootStorageURI() +{ + return m_sBaseURI; +} + +OUString SAL_CALL +ScriptingFrameworkURIHelper::getImplementationName() +{ + return + "com.sun.star.script.provider.ScriptURIHelper"; +} + +sal_Bool SAL_CALL +ScriptingFrameworkURIHelper::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService( this, serviceName ); +} + +uno::Sequence< OUString > SAL_CALL +ScriptingFrameworkURIHelper::getSupportedServiceNames() +{ + return { "com.sun.star.script.provider.ScriptURIHelper" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_ScriptingFrameworkURIHelper_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new ScriptingFrameworkURIHelper(context)); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/provider/URIHelper.hxx b/scripting/source/provider/URIHelper.hxx new file mode 100644 index 000000000..9d200a1a3 --- /dev/null +++ b/scripting/source/provider/URIHelper.hxx @@ -0,0 +1,87 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/script/provider/XScriptURIHelper.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/ucb/XSimpleFileAccess3.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> + +#include <rtl/ustring.hxx> +#include <cppuhelper/implbase.hxx> + +namespace func_provider +{ + +class ScriptingFrameworkURIHelper : + public ::cppu::WeakImplHelper< + css::script::provider::XScriptURIHelper, + css::lang::XServiceInfo, + css::lang::XInitialization > +{ +private: + + css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSimpleFileAccess; + css::uno::Reference<css::uri::XUriReferenceFactory> m_xUriReferenceFactory; + + OUString m_sLanguage; + OUString m_sLocation; + OUString m_sBaseURI; + + OUString SCRIPTS_PART; + + bool initBaseURI(); + OUString getLanguagePart(std::u16string_view rStorageURI); + static OUString getLanguagePath(const OUString& rLanguagePart); + +public: + /// @throws css::uno::RuntimeException + explicit ScriptingFrameworkURIHelper( + const css::uno::Reference< css::uno::XComponentContext >& xContext ); + + virtual ~ScriptingFrameworkURIHelper() override; + + virtual void SAL_CALL + initialize( const css::uno::Sequence < css::uno::Any > & args ) override; + + virtual OUString SAL_CALL + getRootStorageURI() override; + + virtual OUString SAL_CALL + getScriptURI( const OUString& rStorageURI ) override; + + virtual OUString SAL_CALL + getStorageURI( const OUString& rScriptURI ) override; + + virtual OUString SAL_CALL + getImplementationName() override; + + virtual sal_Bool SAL_CALL + supportsService( const OUString& ServiceName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; +}; + +} // namespace func_provider + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/pyprov/mailmerge.README b/scripting/source/pyprov/mailmerge.README new file mode 100644 index 000000000..6b4fb5ba4 --- /dev/null +++ b/scripting/source/pyprov/mailmerge.README @@ -0,0 +1,18 @@ +Easiest way I find to test this is to... + +1) + +a) install fakemail and run it +b) tools->options->writer->mail merge email +c) localhost 8025 + +2) + +a) type some text into writer that will exercise utf-8, e.g. "Caolán's test" +b) tools->mail merge wizard->next->email message->select address book +c) create, add one user with your own email address, ok +d) next, next, text, send merged document as email +e) and test all of plain text, html and the various attachment options + +fake mail will dump the mail it gets into its pwd, if all that works, you can +then try with your own normal mail server. diff --git a/scripting/source/pyprov/mailmerge.component b/scripting/source/pyprov/mailmerge.component new file mode 100644 index 000000000..8a9325341 --- /dev/null +++ b/scripting/source/pyprov/mailmerge.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.Python" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="org.openoffice.pyuno.MailMessage"> + <service name="com.sun.star.mail.MailMessage"/> + </implementation> + <implementation name="org.openoffice.pyuno.MailServiceProvider"> + <service name="com.sun.star.mail.MailServiceProvider"/> + </implementation> +</component> diff --git a/scripting/source/pyprov/mailmerge.py b/scripting/source/pyprov/mailmerge.py new file mode 100644 index 000000000..0ef37b477 --- /dev/null +++ b/scripting/source/pyprov/mailmerge.py @@ -0,0 +1,503 @@ +# Caolan McNamara caolanm@redhat.com +# a simple email mailmerge component + +# manual installation for hackers, not necessary for users +# cp mailmerge.py /usr/lib/libreoffice/program +# cd /usr/lib/libreoffice/program +# ./unopkg add --shared mailmerge.py +# edit ~/.openoffice.org2/user/registry/data/org/openoffice/Office/Writer.xcu +# and change EMailSupported to as follows... +# <prop oor:name="EMailSupported" oor:type="xs:boolean"> +# <value>true</value> +# </prop> + +import unohelper +import uno +import re +import os +import encodings.idna + +#to implement com::sun::star::mail::XMailServiceProvider +#and +#to implement com.sun.star.mail.XMailMessage + +from com.sun.star.mail import XMailServiceProvider +from com.sun.star.mail import XMailService +from com.sun.star.mail import XSmtpService +from com.sun.star.mail import XConnectionListener +from com.sun.star.mail import XAuthenticator +from com.sun.star.mail import XMailMessage +from com.sun.star.mail.MailServiceType import SMTP +from com.sun.star.mail.MailServiceType import POP3 +from com.sun.star.mail.MailServiceType import IMAP +from com.sun.star.uno import XCurrentContext +from com.sun.star.lang import IllegalArgumentException +from com.sun.star.lang import EventObject +from com.sun.star.lang import XServiceInfo +from com.sun.star.mail import SendMailMessageFailedException + +from email.mime.base import MIMEBase +from email.message import Message +from email.charset import Charset +from email.charset import QP +from email.encoders import encode_base64 +from email.header import Header +from email.mime.multipart import MIMEMultipart +from email.utils import formatdate +from email.utils import parseaddr +from socket import _GLOBAL_DEFAULT_TIMEOUT + +import sys, smtplib, imaplib, poplib +dbg = False + +# pythonloader looks for a static g_ImplementationHelper variable +g_ImplementationHelper = unohelper.ImplementationHelper() +g_providerImplName = "org.openoffice.pyuno.MailServiceProvider" +g_messageImplName = "org.openoffice.pyuno.MailMessage" + +class PyMailSMTPService(unohelper.Base, XSmtpService): + def __init__( self, ctx ): + self.ctx = ctx + self.listeners = [] + self.supportedtypes = ('Insecure', 'Ssl') + self.server = None + self.connectioncontext = None + self.notify = EventObject(self) + if dbg: + print("PyMailSMTPService init", file=sys.stderr) + print("python version is: " + sys.version, file=sys.stderr) + def addConnectionListener(self, xListener): + if dbg: + print("PyMailSMTPService addConnectionListener", file=sys.stderr) + self.listeners.append(xListener) + def removeConnectionListener(self, xListener): + if dbg: + print("PyMailSMTPService removeConnectionListener", file=sys.stderr) + self.listeners.remove(xListener) + def getSupportedConnectionTypes(self): + if dbg: + print("PyMailSMTPService getSupportedConnectionTypes", file=sys.stderr) + return self.supportedtypes + def connect(self, xConnectionContext, xAuthenticator): + self.connectioncontext = xConnectionContext + if dbg: + print("PyMailSMTPService connect", file=sys.stderr) + server = xConnectionContext.getValueByName("ServerName").strip() + if dbg: + print("ServerName: " + server, file=sys.stderr) + port = int(xConnectionContext.getValueByName("Port")) + if dbg: + print("Port: " + str(port), file=sys.stderr) + tout = xConnectionContext.getValueByName("Timeout") + if dbg: + print(isinstance(tout,int), file=sys.stderr) + if not isinstance(tout,int): + tout = _GLOBAL_DEFAULT_TIMEOUT + if dbg: + print("Timeout: " + str(tout), file=sys.stderr) + if port == 465: + self.server = smtplib.SMTP_SSL(server, port,timeout=tout) + else: + self.server = smtplib.SMTP(server, port,timeout=tout) + + if dbg: + self.server.set_debuglevel(1) + + connectiontype = xConnectionContext.getValueByName("ConnectionType") + if dbg: + print("ConnectionType: " + connectiontype, file=sys.stderr) + if connectiontype.upper() == 'SSL' and port != 465: + self.server.ehlo() + self.server.starttls() + self.server.ehlo() + + user = xAuthenticator.getUserName() + password = xAuthenticator.getPassword() + if user != '': + if dbg: + print("Logging in, username of: " + user, file=sys.stderr) + self.server.login(user, password) + + for listener in self.listeners: + listener.connected(self.notify) + def disconnect(self): + if dbg: + print("PyMailSMTPService disconnect", file=sys.stderr) + if self.server: + self.server.quit() + self.server = None + for listener in self.listeners: + listener.disconnected(self.notify) + def isConnected(self): + if dbg: + print("PyMailSMTPService isConnected", file=sys.stderr) + return self.server != None + def getCurrentConnectionContext(self): + if dbg: + print("PyMailSMTPService getCurrentConnectionContext", file=sys.stderr) + return self.connectioncontext + def sendMailMessage(self, xMailMessage): + COMMASPACE = ', ' + + if dbg: + print("PyMailSMTPService sendMailMessage", file=sys.stderr) + recipients = xMailMessage.getRecipients() + sendermail = xMailMessage.SenderAddress + sendername = xMailMessage.SenderName + subject = xMailMessage.Subject + ccrecipients = xMailMessage.getCcRecipients() + bccrecipients = xMailMessage.getBccRecipients() + if dbg: + print("PyMailSMTPService subject: " + subject, file=sys.stderr) + print("PyMailSMTPService from: " + sendername, file=sys.stderr) + print("PyMailSMTPService from: " + sendermail, file=sys.stderr) + print("PyMailSMTPService send to: %s" % (recipients,), file=sys.stderr) + + attachments = xMailMessage.getAttachments() + + textmsg = Message() + + content = xMailMessage.Body + flavors = content.getTransferDataFlavors() + if dbg: + print("PyMailSMTPService flavors len: %d" % (len(flavors),), file=sys.stderr) + + #Use first flavor that's sane for an email body + for flavor in flavors: + if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find('text/plain') != -1: + if dbg: + print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=sys.stderr) + textbody = content.getTransferData(flavor) + + if len(textbody): + mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType) + if mimeEncoding.find('charset=UTF-8') == -1: + mimeEncoding = mimeEncoding + "; charset=UTF-8" + textmsg['Content-Type'] = mimeEncoding + textmsg['MIME-Version'] = '1.0' + + try: + #it's a string, get it as utf-8 bytes + textbody = textbody.encode('utf-8') + except: + #it's a bytesequence, get raw bytes + textbody = textbody.value + textbody = textbody.decode('utf-8') + c = Charset('utf-8') + c.body_encoding = QP + textmsg.set_payload(textbody, c) + + break + + if (len(attachments)): + msg = MIMEMultipart() + msg.epilogue = '' + msg.attach(textmsg) + else: + msg = textmsg + + hdr = Header(sendername, 'utf-8') + hdr.append('<'+sendermail+'>','us-ascii') + msg['Subject'] = subject + msg['From'] = hdr + msg['To'] = COMMASPACE.join(recipients) + if len(ccrecipients): + msg['Cc'] = COMMASPACE.join(ccrecipients) + if xMailMessage.ReplyToAddress != '': + msg['Reply-To'] = xMailMessage.ReplyToAddress + + mailerstring = "LibreOffice via Caolan's mailmerge component" + try: + ctx = uno.getComponentContext() + aConfigProvider = ctx.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider") + prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue') + prop.Name = "nodepath" + prop.Value = "/org.openoffice.Setup/Product" + aSettings = aConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", + (prop,)) + mailerstring = aSettings.getByName("ooName") + " " + \ + aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component" + except: + pass + + msg['X-Mailer'] = mailerstring + msg['Date'] = formatdate(localtime=True) + + for attachment in attachments: + content = attachment.Data + flavors = content.getTransferDataFlavors() + flavor = flavors[0] + ctype = flavor.MimeType + maintype, subtype = ctype.split('/', 1) + msgattachment = MIMEBase(maintype, subtype) + data = content.getTransferData(flavor) + msgattachment.set_payload(data.value) + encode_base64(msgattachment) + fname = attachment.ReadableName + try: + msgattachment.add_header('Content-Disposition', 'attachment', \ + filename=fname) + except: + msgattachment.add_header('Content-Disposition', 'attachment', \ + filename=('utf-8','',fname)) + if dbg: + print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=sys.stderr) + + msg.attach(msgattachment) + + uniquer = {} + for key in recipients: + uniquer[key] = True + if len(ccrecipients): + for key in ccrecipients: + uniquer[key] = True + if len(bccrecipients): + for key in bccrecipients: + uniquer[key] = True + truerecipients = uniquer.keys() + + if dbg: + print(("PyMailSMTPService recipients are: ", truerecipients), file=sys.stderr) + + self.server.sendmail(sendermail, truerecipients, msg.as_string()) + +class PyMailIMAPService(unohelper.Base, XMailService): + def __init__( self, ctx ): + self.ctx = ctx + self.listeners = [] + self.supportedtypes = ('Insecure', 'Ssl') + self.server = None + self.connectioncontext = None + self.notify = EventObject(self) + if dbg: + print("PyMailIMAPService init", file=sys.stderr) + def addConnectionListener(self, xListener): + if dbg: + print("PyMailIMAPService addConnectionListener", file=sys.stderr) + self.listeners.append(xListener) + def removeConnectionListener(self, xListener): + if dbg: + print("PyMailIMAPService removeConnectionListener", file=sys.stderr) + self.listeners.remove(xListener) + def getSupportedConnectionTypes(self): + if dbg: + print("PyMailIMAPService getSupportedConnectionTypes", file=sys.stderr) + return self.supportedtypes + def connect(self, xConnectionContext, xAuthenticator): + if dbg: + print("PyMailIMAPService connect", file=sys.stderr) + + self.connectioncontext = xConnectionContext + server = xConnectionContext.getValueByName("ServerName") + if dbg: + print(server, file=sys.stderr) + port = int(xConnectionContext.getValueByName("Port")) + if dbg: + print(port, file=sys.stderr) + connectiontype = xConnectionContext.getValueByName("ConnectionType") + if dbg: + print(connectiontype, file=sys.stderr) + print("BEFORE", file=sys.stderr) + if connectiontype.upper() == 'SSL': + self.server = imaplib.IMAP4_SSL(server, port) + else: + self.server = imaplib.IMAP4(server, port) + print("AFTER", file=sys.stderr) + + user = xAuthenticator.getUserName() + password = xAuthenticator.getPassword() + if user != '': + if dbg: + print("Logging in, username of: " + user, file=sys.stderr) + self.server.login(user, password) + + for listener in self.listeners: + listener.connected(self.notify) + def disconnect(self): + if dbg: + print("PyMailIMAPService disconnect", file=sys.stderr) + if self.server: + self.server.logout() + self.server = None + for listener in self.listeners: + listener.disconnected(self.notify) + def isConnected(self): + if dbg: + print("PyMailIMAPService isConnected", file=sys.stderr) + return self.server != None + def getCurrentConnectionContext(self): + if dbg: + print("PyMailIMAPService getCurrentConnectionContext", file=sys.stderr) + return self.connectioncontext + +class PyMailPOP3Service(unohelper.Base, XMailService): + def __init__( self, ctx ): + self.ctx = ctx + self.listeners = [] + self.supportedtypes = ('Insecure', 'Ssl') + self.server = None + self.connectioncontext = None + self.notify = EventObject(self) + if dbg: + print("PyMailPOP3Service init", file=sys.stderr) + def addConnectionListener(self, xListener): + if dbg: + print("PyMailPOP3Service addConnectionListener", file=sys.stderr) + self.listeners.append(xListener) + def removeConnectionListener(self, xListener): + if dbg: + print("PyMailPOP3Service removeConnectionListener", file=sys.stderr) + self.listeners.remove(xListener) + def getSupportedConnectionTypes(self): + if dbg: + print("PyMailPOP3Service getSupportedConnectionTypes", file=sys.stderr) + return self.supportedtypes + def connect(self, xConnectionContext, xAuthenticator): + if dbg: + print("PyMailPOP3Service connect", file=sys.stderr) + + self.connectioncontext = xConnectionContext + server = xConnectionContext.getValueByName("ServerName") + if dbg: + print(server, file=sys.stderr) + port = int(xConnectionContext.getValueByName("Port")) + if dbg: + print(port, file=sys.stderr) + connectiontype = xConnectionContext.getValueByName("ConnectionType") + if dbg: + print(connectiontype, file=sys.stderr) + print("BEFORE", file=sys.stderr) + if connectiontype.upper() == 'SSL': + self.server = poplib.POP3_SSL(server, port) + else: + tout = xConnectionContext.getValueByName("Timeout") + if dbg: + print(isinstance(tout,int), file=sys.stderr) + if not isinstance(tout,int): + tout = _GLOBAL_DEFAULT_TIMEOUT + if dbg: + print("Timeout: " + str(tout), file=sys.stderr) + self.server = poplib.POP3(server, port, timeout=tout) + print("AFTER", file=sys.stderr) + + user = xAuthenticator.getUserName() + password = xAuthenticator.getPassword() + if dbg: + print("Logging in, username of: " + user, file=sys.stderr) + self.server.user(user) + self.server.pass_(password) + + for listener in self.listeners: + listener.connected(self.notify) + def disconnect(self): + if dbg: + print("PyMailPOP3Service disconnect", file=sys.stderr) + if self.server: + self.server.quit() + self.server = None + for listener in self.listeners: + listener.disconnected(self.notify) + def isConnected(self): + if dbg: + print("PyMailPOP3Service isConnected", file=sys.stderr) + return self.server != None + def getCurrentConnectionContext(self): + if dbg: + print("PyMailPOP3Service getCurrentConnectionContext", file=sys.stderr) + return self.connectioncontext + +class PyMailServiceProvider(unohelper.Base, XMailServiceProvider, XServiceInfo): + def __init__( self, ctx ): + if dbg: + print("PyMailServiceProvider init", file=sys.stderr) + self.ctx = ctx + def create(self, aType): + if dbg: + print("PyMailServiceProvider create with", aType, file=sys.stderr) + if aType == SMTP: + return PyMailSMTPService(self.ctx); + elif aType == POP3: + return PyMailPOP3Service(self.ctx); + elif aType == IMAP: + return PyMailIMAPService(self.ctx); + else: + print("PyMailServiceProvider, unknown TYPE " + aType, file=sys.stderr) + + def getImplementationName(self): + return g_providerImplName + + def supportsService(self, ServiceName): + return g_ImplementationHelper.supportsService(g_providerImplName, ServiceName) + + def getSupportedServiceNames(self): + return g_ImplementationHelper.getSupportedServiceNames(g_providerImplName) + +class PyMailMessage(unohelper.Base, XMailMessage): + def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ): + if dbg: + print("PyMailMessage init", file=sys.stderr) + self.ctx = ctx + + self.recipients = [sTo] + self.ccrecipients = [] + self.bccrecipients = [] + self.aMailAttachments = [] + if aMailAttachment != None: + self.aMailAttachments.append(aMailAttachment) + + self.SenderName, self.SenderAddress = parseaddr(sFrom) + self.ReplyToAddress = sFrom + self.Subject = Subject + self.Body = Body + if dbg: + print("post PyMailMessage init", file=sys.stderr) + def addRecipient( self, recipient ): + if dbg: + print("PyMailMessage.addRecipient: " + recipient, file=sys.stderr) + self.recipients.append(recipient) + def addCcRecipient( self, ccrecipient ): + if dbg: + print("PyMailMessage.addCcRecipient: " + ccrecipient, file=sys.stderr) + self.ccrecipients.append(ccrecipient) + def addBccRecipient( self, bccrecipient ): + if dbg: + print("PyMailMessage.addBccRecipient: " + bccrecipient, file=sys.stderr) + self.bccrecipients.append(bccrecipient) + def getRecipients( self ): + if dbg: + print("PyMailMessage.getRecipients: " + str(self.recipients), file=sys.stderr) + return tuple(self.recipients) + def getCcRecipients( self ): + if dbg: + print("PyMailMessage.getCcRecipients: " + str(self.ccrecipients), file=sys.stderr) + return tuple(self.ccrecipients) + def getBccRecipients( self ): + if dbg: + print("PyMailMessage.getBccRecipients: " + str(self.bccrecipients), file=sys.stderr) + return tuple(self.bccrecipients) + def addAttachment( self, aMailAttachment ): + if dbg: + print("PyMailMessage.addAttachment", file=sys.stderr) + self.aMailAttachments.append(aMailAttachment) + def getAttachments( self ): + if dbg: + print("PyMailMessage.getAttachments", file=sys.stderr) + return tuple(self.aMailAttachments) + + def getImplementationName(self): + return g_messageImplName + + def supportsService(self, ServiceName): + return g_ImplementationHelper.supportsService(g_messageImplName, ServiceName) + + def getSupportedServiceNames(self): + return g_ImplementationHelper.getSupportedServiceNames(g_messageImplName) + +g_ImplementationHelper.addImplementation( \ + PyMailServiceProvider, g_providerImplName, + ("com.sun.star.mail.MailServiceProvider",),) +g_ImplementationHelper.addImplementation( \ + PyMailMessage, g_messageImplName, + ("com.sun.star.mail.MailMessage",),) + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/source/pyprov/msgbox.py b/scripting/source/pyprov/msgbox.py new file mode 100644 index 000000000..f9c93df17 --- /dev/null +++ b/scripting/source/pyprov/msgbox.py @@ -0,0 +1,241 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/. +# + +# prepare Python environment - Add the path of this class +from os import path +from sys import modules +from sys import path as syspath + +# pyUNO program itself +import uno, unohelper + +# UNO GUI toolkit +from com.sun.star.awt.WindowClass import TOP, SIMPLE +from com.sun.star.awt.PushButtonType import STANDARD as standard +from com.sun.star.awt.PushButtonType import OK as ok +from com.sun.star.awt.PushButtonType import CANCEL as cancel +from com.sun.star.awt.PushButtonType import HELP as help +from com.sun.star.awt.TextAlign import CENTER as center +from com.sun.star.awt.TextAlign import LEFT as left +from com.sun.star.awt.TextAlign import RIGHT as right + +# used UNO listeners +from com.sun.star.awt import XActionListener + +class MsgBox(unohelper.Base): + """Inspect UNO object, link to sdk and recursive calls""" + + def __init__(self, aContext): + """acontext : a Valid UNO context + """ + + self.VERSION = '0.1' + self.ctx = aContext + self.smgr = aContext.ServiceManager + # UI Dialog object + self.dialog=None + # List of opened Listeners + self.lst_listeners={} + #UI parameters + self.ButtonSize = 50 + self.boxSize = 200 + self.lineHeight = 10 + self.fromBroxSize = False + self.numberOfLines = -1 + + self.Buttons = [] + self.Response = '' + + return + + ##################################################### + # GUI definition # + ##################################################### + def _createBox(self): + """Create the Box""" + + # computes parameters of the message dialog + if self.numberOfLines == -1: + #calculate + numberOfLines = len(self.message.split(chr(10))) + else: + numberOfLines = self.numberOfLines + + numberOfButtons = len(self.Buttons) + self.ButtonSpace = self.ButtonSize/2 + if self.fromBroxSize: + # button size is calculated from boxsize + size = (2 * self.boxSize) / (3 * numberOfButtons + 1) + self.ButtonSize = size + self.ButtonSpace = self.ButtonSize/2 + else: + # boxsize is calculated from buttonsize + self.boxSize = numberOfButtons * (self.ButtonSize + + self.ButtonSpace) + self.ButtonSpace + + # create the dialog model and set the properties + dialog_model = self.smgr.createInstanceWithContext( + 'com.sun.star.awt.UnoControlDialogModel', + self.ctx) + dialog_model.PositionX = 50 + dialog_model.Step = 1 + dialog_model.TabIndex = 7 + dialog_model.Width = self.boxSize#numberOfButtons * (self.ButtonSize + + # self.ButtonSpace) + 25 + dialog_model.Height = 10 + self.lineHeight * numberOfLines + 10 + 12 + 10 + dialog_model.PositionY = 63 + dialog_model.Sizeable = True + dialog_model.Closeable = False + + dialog = self.smgr.createInstanceWithContext( + 'com.sun.star.awt.UnoControlDialog', self.ctx) + + # label Label0 + label = dialog_model.createInstance( + 'com.sun.star.awt.UnoControlFixedTextModel') + label.PositionX = 10 + label.TabIndex = 9 + label.Width = dialog_model.Width - label.PositionX + label.Height = self.lineHeight* numberOfLines + label.PositionY = 10 + label.Align = left + label.MultiLine = True + label.Label = self.message + dialog_model.insertByName('Label0', label) + + nb = 0 + for buttonName in self.Buttons: + nb +=1 + button = dialog_model.createInstance( + 'com.sun.star.awt.UnoControlButtonModel') + button.PositionX = nb * self.ButtonSpace + (nb-1)* self.ButtonSize + button.TabIndex = 8 + button.Height = 12 + button.Width = self.ButtonSize + button.PositionY = 10 + label.Height + 10 + button.PushButtonType = standard + if nb == 1: + button.DefaultButton = True + else: + button.DefaultButton = False + button.Label = buttonName + dialog_model.insertByName('Btn' + str(nb), button ) + + if not dialog.getModel(): + dialog.setModel(dialog_model) + + # UNO toolkit definition + toolkit = self.smgr.createInstanceWithContext('com.sun.star.awt.Toolkit', self.ctx) + a_rect = uno.createUnoStruct( 'com.sun.star.awt.Rectangle' ) + a_rect.X = 50 + dialog.setTitle ( self.title ) + a_rect.Width = 270 + a_rect.Height = 261 + a_rect.Y = 63 + win_descriptor = uno.createUnoStruct('com.sun.star.awt.WindowDescriptor') + win_descriptor.Type = TOP + win_descriptor.ParentIndex = -1 + win_descriptor.Bounds = a_rect + peer = toolkit.createWindow( win_descriptor ) + dialog.createPeer( toolkit, peer ) + + return dialog + + def _addListeners(self): + """Add listeners to dialog""" + nb = 0 + for buttonName in self.Buttons: + nb +=1 + a_control = self.dialog.getControl('Btn'+str(nb)) + the_listener = ButtonListener(self) + a_control.addActionListener(the_listener) + self.lst_listeners['Btn'+str(nb)] = the_listener + return + + def _removeListeners(self): + """ remove listeners on exiting""" + nb = 0 + for buttonName in self.Buttons: + nb +=1 + a_control = self.dialog.getControl('Btn'+str(nb)) + a_control.removeActionListener(self.lst_listeners['Btn'+str(nb)]) + return + + def show(self, message, decoration, title): + self.message = message + self.decoration = decoration + self.title = title + # Create GUI + self.dialog = self._createBox() + self._addListeners() + #execute the dialog --> blocking call + self.dialog.execute() + #end --> release listeners and dispose dialog + self._removeListeners() + self.dialog.dispose() + return self.Response + + def addButton(self, caption): + self.Buttons.append(caption) + return + + def renderFromBoxSize(self, size = 150): + self.boxSize = size + self.fromBroxSize = True + return + + def renderFromButtonSize(self, size = 50): + self.ButtonSize = size + self.fromBroxSize = False + return + +class ButtonListener(unohelper.Base, XActionListener): + """Stops the MessageBox, sets the button label as returned value""" + def __init__(self, caller): + self.caller = caller + + def disposing(self, eventObject): + pass + + def actionPerformed(self, actionEvent): + button = actionEvent.Source + self.caller.Response = button.Model.Label + self.caller.dialog.endExecute() + return + +### TEST +if __name__ == '__main__': + # get the uno component context from the PyUNO runtime + localContext = uno.getComponentContext() + + # create the UnoUrlResolver + resolver = localContext.ServiceManager.createInstanceWithContext( + "com.sun.star.bridge.UnoUrlResolver", localContext ) + + # connect to the running office + # LibO has to be launched in listen mode as + # ./soffice "--accept=socket,host=localhost,port=2002;urp;" + ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" ) + myBox = MsgBox(ctx) + myBox.addButton("Yes") + myBox.addButton("No") + myBox.addButton("May be") + myBox.renderFromBoxSize(150) + myBox.numberOflines = 2 + + print(myBox.show("A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message " + chr(10)+chr(10)+"Do you agree ?",0,"Dialog title")) + + myBox = MsgBox(ctx) + myBox.addButton("oK") + myBox.renderFromButtonSize() + myBox.numberOflines = 2 + + print(myBox.show("A small message",0,"Dialog title")) + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/source/pyprov/pythonscript.py b/scripting/source/pyprov/pythonscript.py new file mode 100644 index 000000000..4955c8c54 --- /dev/null +++ b/scripting/source/pyprov/pythonscript.py @@ -0,0 +1,1145 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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 . +# +# XScript implementation for python +import uno +import unohelper +import sys +import os +import types +import time +import ast +import platform +from com.sun.star.uri.RelativeUriExcessParentSegments import RETAIN + +class LogLevel: + NONE = 0 # production level + ERROR = 1 # for script developers + DEBUG = 2 # for script framework developers + +PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL" +PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT" + +# Configuration ---------------------------------------------------- +LogLevel.use = LogLevel.NONE +if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR": + LogLevel.use = LogLevel.ERROR +elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG": + LogLevel.use = LogLevel.DEBUG + +# True, writes to stdout (difficult on windows) +# False, writes to user/Scripts/python/log.txt +LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0" + +ENABLE_EDIT_DIALOG=False # offers a minimal editor for editing. +#------------------------------------------------------------------- + +def encfile(uni): + return uni.encode( sys.getfilesystemencoding()) + +def lastException2String(): + (excType,excInstance,excTraceback) = sys.exc_info() + ret = str(excType) + ": "+str(excInstance) + "\n" + \ + uno._uno_extract_printable_stacktrace( excTraceback ) + return ret + +def logLevel2String( level ): + ret = " NONE" + if level == LogLevel.ERROR: + ret = "ERROR" + elif level >= LogLevel.DEBUG: + ret = "DEBUG" + return ret + +def getLogTarget(): + ret = sys.stdout + if not LOG_STDOUT: + try: + pathSubst = uno.getComponentContext().ServiceManager.createInstance( + "com.sun.star.util.PathSubstitution" ) + userInstallation = pathSubst.getSubstituteVariableValue( "user" ) + if len( userInstallation ) > 0: + systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" ) + ret = open( systemPath , "a" ) + except: + print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delegating log to stdout\n") + return ret + +class Logger(LogLevel): + def __init__(self , target ): + self.target = target + + def isDebugLevel( self ): + return self.use >= self.DEBUG + + def debug( self, msg ): + if self.isDebugLevel(): + self.log( self.DEBUG, msg ) + + def isErrorLevel( self ): + return self.use >= self.ERROR + + def error( self, msg ): + if self.isErrorLevel(): + self.log( self.ERROR, msg ) + + def log( self, level, msg ): + if self.use >= level: + try: + self.target.write( + time.asctime() + + " [" + + logLevel2String( level ) + + "] " + + msg + + "\n" ) + self.target.flush() + except: + print("Error during writing to stdout: " +lastException2String() + "\n") + +log = Logger( getLogTarget() ) + +log.debug( "pythonscript loading" ) + +#from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider +from com.sun.star.uno import RuntimeException +from com.sun.star.lang import IllegalArgumentException +from com.sun.star.container import NoSuchElementException +from com.sun.star.lang import XServiceInfo +from com.sun.star.io import IOException +from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command +from com.sun.star.task import XInteractionHandler +from com.sun.star.beans import XPropertySet, Property +from com.sun.star.container import XNameContainer +from com.sun.star.xml.sax import XDocumentHandler, InputSource +from com.sun.star.uno import Exception as UnoException +from com.sun.star.script import XInvocation +from com.sun.star.awt import XActionListener + +from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException +from com.sun.star.script.browse import XBrowseNode +from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT +from com.sun.star.util import XModifyListener + +LANGUAGENAME = "Python" +GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT" +CALLABLE_CONTAINER_NAME = "g_exportedScripts" + +# pythonloader looks for a static g_ImplementationHelper variable +g_ImplementationHelper = unohelper.ImplementationHelper() +g_implName = "org.libreoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME + + + +BLOCK_SIZE = 65536 +def readTextFromStream( inputStream ): + # read the file + code = uno.ByteSequence( b"" ) + while True: + read,out = inputStream.readBytes( None , BLOCK_SIZE ) + code = code + out + if read < BLOCK_SIZE: + break + return code.value + +def toIniName( str ): + if platform.system() == "Windows": + return str + ".ini" + else: + return str + "rc" + + +""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk + scriptURI is the system independent uri +""" +class MyUriHelper: + + def __init__( self, ctx, location ): + self.ctx = ctx + self.s_UriMap = \ + { "share" : "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python" , \ + "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \ + "user" : "vnd.sun.star.expand:${$BRAND_INI_DIR/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \ + "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" } + self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx) + if location.startswith( "vnd.sun.star.tdoc" ): + self.m_baseUri = location + "/Scripts/python" + self.m_scriptUriLocation = "document" + else: + self.m_baseUri = expandUri( self.s_UriMap[location] ) + self.m_scriptUriLocation = location + log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation ) + + def getRootStorageURI( self ): + return self.m_baseUri + + def getStorageURI( self, scriptURI ): + return self.scriptURI2StorageUri(scriptURI) + + def getScriptURI( self, storageURI ): + return self.storageURI2ScriptUri(storageURI) + + def storageURI2ScriptUri( self, storageURI ): + if not storageURI.startswith( self.m_baseUri ): + message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'" + log.debug( message ) + raise RuntimeException( message, self.ctx ) + + ret = "vnd.sun.star.script:" + \ + storageURI[len(self.m_baseUri)+1:].replace("/","|") + \ + "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation + log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret ) + return ret + + def scriptURI2StorageUri( self, scriptURI ): + try: + # base path to the python script location + sBaseUri = self.m_baseUri + "/" + xBaseUri = self.m_uriRefFac.parse(sBaseUri) + + # path to the .py file + "$functionname, arguments, etc + xStorageUri = self.m_uriRefFac.parse(scriptURI) + # getName will apply url-decoding to the name, so encode back + sStorageUri = xStorageUri.getName().replace("%", "%25") + sStorageUri = sStorageUri.replace( "|", "/" ) + + # path to the .py file, relative to the base + funcNameStart = sStorageUri.find("$") + if funcNameStart != -1: + sFileUri = sStorageUri[0:funcNameStart] + sFuncName = sStorageUri[funcNameStart+1:] + else: + sFileUri = sStorageUri + + xFileUri = self.m_uriRefFac.parse(sFileUri) + if not xFileUri: + message = "pythonscript: invalid relative uri '" + sFileUri+ "'" + log.debug( message ) + raise RuntimeException( message, self.ctx ) + + if not xFileUri.hasRelativePath(): + message = "pythonscript: an absolute uri is invalid '" + sFileUri+ "'" + log.debug( message ) + raise RuntimeException( message, self.ctx ) + + # absolute path to the .py file + xAbsScriptUri = self.m_uriRefFac.makeAbsolute(xBaseUri, xFileUri, True, RETAIN) + sAbsScriptUri = xAbsScriptUri.getUriReference() + + # ensure py file is under the base path + if not sAbsScriptUri.startswith(sBaseUri): + message = "pythonscript: storage uri '" + sAbsScriptUri + "' not in base uri '" + self.m_baseUri + "'" + log.debug( message ) + raise RuntimeException( message, self.ctx ) + + ret = sAbsScriptUri + if funcNameStart != -1: + ret = ret + "$" + sFuncName + log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret ) + return ret + except UnoException as e: + log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message) + raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + e.Message, self.ctx ) + except Exception as e: + log.error( "error during converting scriptURI="+scriptURI + ": " + str(e)) + raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), self.ctx ) + + +class ModuleEntry: + def __init__( self, lastRead, module ): + self.lastRead = lastRead + self.module = module + +def hasChanged( oldDate, newDate ): + return newDate.Year > oldDate.Year or \ + newDate.Month > oldDate.Month or \ + newDate.Day > oldDate.Day or \ + newDate.Hours > oldDate.Hours or \ + newDate.Minutes > oldDate.Minutes or \ + newDate.Seconds > oldDate.Seconds or \ + newDate.NanoSeconds > oldDate.NanoSeconds + +def ensureSourceState( code ): + if code.endswith(b"\n"): + code = code + b"\n" + code = code.replace(b"\r", b"") + return code + + +def checkForPythonPathBesideScript( url ): + if url.startswith( "file:" ): + path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" ); + log.log( LogLevel.DEBUG, "checking for existence of " + path ) + if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path: + log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" ) + sys.path.append( path ) + + path = unohelper.fileUrlToSystemPath( url+"/pythonpath" ); + log.log( LogLevel.DEBUG, "checking for existence of " + path ) + if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path: + log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" ) + sys.path.append( path ) + + +class ScriptContext(unohelper.Base): + def __init__( self, ctx, doc, inv ): + self.ctx = ctx + self.doc = doc + self.inv = inv + + # XScriptContext + def getDocument(self): + if self.doc: + return self.doc + return self.getDesktop().getCurrentComponent() + + def getDesktop(self): + return self.ctx.ServiceManager.createInstanceWithContext( + "com.sun.star.frame.Desktop", self.ctx ) + + def getComponentContext(self): + return self.ctx + + def getInvocationContext(self): + return self.inv + +#---------------------------------- +# Global Module Administration +# does not fit together with script +# engine lifetime management +#---------------------------------- +#g_scriptContext = ScriptContext( uno.getComponentContext(), None ) +#g_modules = {} +#def getModuleByUrl( url, sfa ): +# entry = g_modules.get(url) +# load = True +# lastRead = sfa.getDateTimeModified( url ) +# if entry: +# if hasChanged( entry.lastRead, lastRead ): +# log.debug("file " + url + " has changed, reloading") +# else: +# load = False +# +# if load: +# log.debug( "opening >" + url + "<" ) +# +# code = readTextFromStream( sfa.openFileRead( url ) ) + + # execute the module +# entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") ) +# entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext +# entry.module.__file__ = url +# exec code in entry.module.__dict__ +# g_modules[ url ] = entry +# log.debug( "mapped " + url + " to " + str( entry.module ) ) +# return entry.module + +class ProviderContext: + def __init__( self, storageType, sfa, uriHelper, scriptContext ): + self.storageType = storageType + self.sfa = sfa + self.uriHelper = uriHelper + self.scriptContext = scriptContext + self.modules = {} + self.rootUrl = None + self.mapPackageName2Path = None + + def getTransientPartFromUrl( self, url ): + rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1) + return rest[0:rest.find("/")] + + def getPackageNameFromUrl( self, url ): + rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1) + start = rest.find("/") +1 + return rest[start:rest.find("/",start)] + + + def removePackageByUrl( self, url ): + items = self.mapPackageName2Path.items() + for i in items: + if url in i[1].paths: + self.mapPackageName2Path.pop(i[0]) + break + + def addPackageByUrl( self, url ): + packageName = self.getPackageNameFromUrl( url ) + transientPart = self.getTransientPartFromUrl( url ) + log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl ) + if packageName in self.mapPackageName2Path: + package = self.mapPackageName2Path[ packageName ] + package.paths = package.paths + (url, ) + else: + package = Package( (url,), transientPart) + self.mapPackageName2Path[ packageName ] = package + + def isUrlInPackage( self, url ): + values = self.mapPackageName2Path.values() + for i in values: +# print ("checking " + url + " in " + str(i.paths)) + if url in i.paths: + return True +# print ("false") + return False + + def setPackageAttributes( self, mapPackageName2Path, rootUrl ): + self.mapPackageName2Path = mapPackageName2Path + self.rootUrl = rootUrl + + def getPersistentUrlFromStorageUrl( self, url ): + # package name is the second directory + ret = url + if self.rootUrl: + pos = len( self.rootUrl) +1 + ret = url[0:pos]+url[url.find("/",pos)+1:len(url)] + log.debug( "getPersistentUrlFromStorageUrl " + url + " -> "+ ret) + return ret + + def getStorageUrlFromPersistentUrl( self, url): + ret = url + if self.rootUrl: + pos = len(self.rootUrl)+1 + packageName = url[pos:url.find("/",pos+1)] + package = self.mapPackageName2Path[ packageName ] + ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)] + log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret) + return ret + + def getFuncsByUrl( self, url ): + src = readTextFromStream( self.sfa.openFileRead( url ) ) + checkForPythonPathBesideScript( url[0:url.rfind('/')] ) + src = ensureSourceState( src ) + + try: + code = ast.parse( src ) + except: + log.isDebugLevel() and log.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String()) + raise + + allFuncs = [] + + if code is None: + return allFuncs + + g_exportedScripts = [] + for node in ast.iter_child_nodes(code): + if isinstance(node, ast.FunctionDef): + allFuncs.append(node.name) + elif isinstance(node, ast.Assign): + for target in node.targets: + try: + identifier = target.id + except AttributeError: + identifier = "" + pass + if identifier == "g_exportedScripts": + for value in node.value.elts: + g_exportedScripts.append(value.id) + return g_exportedScripts + +# Python 2 only +# for node in code.node.nodes: +# if node.__class__.__name__ == 'Function': +# allFuncs.append(node.name) +# elif node.__class__.__name__ == 'Assign': +# for assignee in node.nodes: +# if assignee.name == 'g_exportedScripts': +# for item in node.expr.nodes: +# if item.__class__.__name__ == 'Name': +# g_exportedScripts.append(item.name) +# return g_exportedScripts + + return allFuncs + + def getModuleByUrl( self, url ): + entry = self.modules.get(url) + load = True + lastRead = self.sfa.getDateTimeModified( url ) + if entry: + if hasChanged( entry.lastRead, lastRead ): + log.debug( "file " + url + " has changed, reloading" ) + else: + load = False + + if load: + log.debug( "opening >" + url + "<" ) + + src = readTextFromStream( self.sfa.openFileRead( url ) ) + checkForPythonPathBesideScript( url[0:url.rfind('/')] ) + src = ensureSourceState( src ) + + # execute the module + entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") ) + entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext + + code = None + if url.startswith( "file:" ): + code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" ) + else: + code = compile( src, url, "exec" ) + exec(code, entry.module.__dict__) + entry.module.__file__ = url + self.modules[ url ] = entry + log.debug( "mapped " + url + " to " + str( entry.module ) ) + return entry.module + +#-------------------------------------------------- +def isScript( candidate ): + ret = False + if isinstance( candidate, type(isScript) ): + ret = True + return ret + +#------------------------------------------------------- +class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ): + def __init__( self, provCtx, uri, fileName, funcName ): + self.fileName = fileName + self.funcName = funcName + self.provCtx = provCtx + self.uri = uri + + def getName( self ): + return self.funcName + + def getChildNodes(self): + return () + + def hasChildNodes(self): + return False + + def getType( self): + return SCRIPT + + def getPropertyValue( self, name ): + ret = None + try: + if name == "URI": + ret = self.provCtx.uriHelper.getScriptURI( + self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) ) + elif name == "Editable" and ENABLE_EDIT_DIALOG: + ret = not self.provCtx.sfa.isReadOnly( self.uri ) + + log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) ) + except: + log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String()) + raise + + return ret + def setPropertyValue( self, name, value ): + log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) ) + def getPropertySetInfo( self ): + log.debug( "ScriptBrowseNode.getPropertySetInfo called " ) + return None + + def getIntrospection( self ): + return None + + def invoke( self, name, params, outparamindex, outparams ): + if name == "Editable": + servicename = "com.sun.star.awt.DialogProvider" + ctx = self.provCtx.scriptContext.getComponentContext() + dlgprov = ctx.ServiceManager.createInstanceWithContext( + servicename, ctx ) + + self.editor = dlgprov.createDialog( + "vnd.sun.star.script:" + + "ScriptBindingLibrary.MacroEditor?location=application") + + code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri)) + code = ensureSourceState( code ) + self.editor.getControl("EditorTextField").setText(code) + + self.editor.getControl("RunButton").setActionCommand("Run") + self.editor.getControl("RunButton").addActionListener(self) + self.editor.getControl("SaveButton").setActionCommand("Save") + self.editor.getControl("SaveButton").addActionListener(self) + + self.editor.execute() + + return None + + def actionPerformed( self, event ): + try: + if event.ActionCommand == "Run": + code = self.editor.getControl("EditorTextField").getText() + code = ensureSourceState( code ) + mod = types.ModuleType("ooo_script_framework") + mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext + exec(code, mod.__dict__) + values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None ) + if not values: + values = mod.__dict__.values() + + for i in values: + if isScript( i ): + i() + break + + elif event.ActionCommand == "Save": + toWrite = uno.ByteSequence( + self.editor.getControl("EditorTextField").getText().encode( + sys.getdefaultencoding()) ) + copyUrl = self.uri + ".orig" + self.provCtx.sfa.move( self.uri, copyUrl ) + out = self.provCtx.sfa.openFileWrite( self.uri ) + out.writeBytes( toWrite ) + out.close() + self.provCtx.sfa.kill( copyUrl ) +# log.debug("Save is not implemented yet") +# text = self.editor.getControl("EditorTextField").getText() +# log.debug("Would save: " + text) + except: + # TODO: add an error box here! + log.error( lastException2String() ) + + + def setValue( self, name, value ): + return None + + def getValue( self, name ): + return None + + def hasMethod( self, name ): + return False + + def hasProperty( self, name ): + return False + + +#------------------------------------------------------- +class FileBrowseNode( unohelper.Base, XBrowseNode ): + def __init__( self, provCtx, uri , name ): + self.provCtx = provCtx + self.uri = uri + self.name = name + self.funcnames = None + + def getName( self ): + return self.name + + def getChildNodes(self): + ret = () + try: + self.funcnames = self.provCtx.getFuncsByUrl( self.uri ) + + scriptNodeList = [] + for i in self.funcnames: + scriptNodeList.append( + ScriptBrowseNode( + self.provCtx, self.uri, self.name, i )) + ret = tuple( scriptNodeList ) + log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri ) + except: + text = lastException2String() + log.error( "Error while evaluating " + self.uri + ":" + text ) + raise + return ret + + def hasChildNodes(self): + try: + return len(self.getChildNodes()) > 0 + except: + return False + + def getType( self): + return CONTAINER + + + +class DirBrowseNode( unohelper.Base, XBrowseNode ): + def __init__( self, provCtx, name, rootUrl ): + self.provCtx = provCtx + self.name = name + self.rootUrl = rootUrl + + def getName( self ): + return self.name + + def getChildNodes( self ): + try: + log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl ) + contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True ) + browseNodeList = [] + for i in contents: + if i.endswith( ".py" ): + log.debug( "adding filenode " + i ) + browseNodeList.append( + FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) ) + elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"): + log.debug( "adding DirBrowseNode " + i ) + browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i)) + return tuple( browseNodeList ) + except Exception as e: + text = lastException2String() + log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl) + log.error( text) + return () + + def hasChildNodes( self ): + return True + + def getType( self ): + return CONTAINER + + def getScript( self, uri ): + log.debug( "DirBrowseNode getScript " + uri + " invoked" ) + raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) + + +class ManifestHandler( XDocumentHandler, unohelper.Base ): + def __init__( self, rootUrl ): + self.rootUrl = rootUrl + + def startDocument( self ): + self.urlList = [] + + def endDocument( self ): + pass + + def startElement( self , name, attlist): + if name == "manifest:file-entry": + if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script": + self.urlList.append( + self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) ) + + def endElement( self, name ): + pass + + def characters ( self, chars ): + pass + + def ignoreableWhitespace( self, chars ): + pass + + def setDocumentLocator( self, locator ): + pass + +def isPyFileInPath( sfa, path ): + ret = False + contents = sfa.getFolderContents( path, True ) + for i in contents: + if sfa.isFolder(i): + ret = isPyFileInPath(sfa,i) + else: + if i.endswith(".py"): + ret = True + if ret: + break + return ret + +# extracts META-INF directory from +def getPathsFromPackage( rootUrl, sfa ): + ret = () + try: + fileUrl = rootUrl + "/META-INF/manifest.xml" + inputStream = sfa.openFileRead( fileUrl ) + parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" ) + handler = ManifestHandler( rootUrl ) + parser.setDocumentHandler( handler ) + parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) ) + for i in tuple(handler.urlList): + if not isPyFileInPath( sfa, i ): + handler.urlList.remove(i) + ret = tuple( handler.urlList ) + except UnoException: + text = lastException2String() + log.debug( "getPathsFromPackage " + fileUrl + " Exception: " +text ) + pass + return ret + + +class Package: + def __init__( self, paths, transientPathElement ): + self.paths = paths + self.transientPathElement = transientPathElement + +class DummyInteractionHandler( unohelper.Base, XInteractionHandler ): + def __init__( self ): + pass + def handle( self, event): + log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) ) + +class DummyProgressHandler( unohelper.Base, XProgressHandler ): + def __init__( self ): + pass + + def push( self,status ): + log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) ) + def update( self,status ): + log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) ) + def pop( self, event ): + log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) ) + +class CommandEnvironment(unohelper.Base, XCommandEnvironment): + def __init__( self ): + self.progressHandler = DummyProgressHandler() + self.interactionHandler = DummyInteractionHandler() + def getInteractionHandler( self ): + return self.interactionHandler + def getProgressHandler( self ): + return self.progressHandler + +#maybe useful for debugging purposes +#class ModifyListener( unohelper.Base, XModifyListener ): +# def __init__( self ): +# pass +# def modified( self, event ): +# log.debug( "pythonscript: ModifyListener.modified " + str( event ) ) +# def disposing( self, event ): +# log.debug( "pythonscript: ModifyListener.disposing " + str( event ) ) + +def getModelFromDocUrl(ctx, url): + """Get document model from document url.""" + doc = None + args = ("Local", "Office") + ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext( + "com.sun.star.ucb.UniversalContentBroker", args, ctx) + identifier = ucb.createContentIdentifier(url) + content = ucb.queryContent(identifier) + p = Property() + p.Name = "DocumentModel" + p.Handle = -1 + + c = Command() + c.Handle = -1 + c.Name = "getPropertyValues" + c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,)) + + env = CommandEnvironment() + try: + ret = content.execute(c, 0, env) + doc = ret.getObject(1, None) + except Exception as e: + log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url) + return doc + +def mapStorageType2PackageContext( storageType ): + ret = storageType + if( storageType == "share:uno_packages" ): + ret = "shared" + if( storageType == "user:uno_packages" ): + ret = "user" + return ret + +def getPackageName2PathMap( sfa, storageType ): + ret = {} + packageManagerFactory = uno.getComponentContext().getValueByName( + "/singletons/com.sun.star.deployment.thePackageManagerFactory" ) + packageManager = packageManagerFactory.getPackageManager( + mapStorageType2PackageContext(storageType)) +# packageManager.addModifyListener( ModifyListener() ) + log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" ) + packages = packageManager.getDeployedPackages( + packageManager.createAbortChannel(), CommandEnvironment( ) ) + log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" ) + + for i in packages: + log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" ) + transientPathElement = penultimateElement( i.URL ) + j = expandUri( i.URL ) + paths = getPathsFromPackage( j, sfa ) + if len( paths ) > 0: + # map package name to url, we need this later + log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( paths ) ) + ret[ lastElement( j ) ] = Package( paths, transientPathElement ) + return ret + +def penultimateElement( aStr ): + lastSlash = aStr.rindex("/") + penultimateSlash = aStr.rindex("/",0,lastSlash-1) + return aStr[ penultimateSlash+1:lastSlash ] + +def lastElement( aStr): + return aStr[ aStr.rfind( "/" )+1:len(aStr)] + +class PackageBrowseNode( unohelper.Base, XBrowseNode ): + def __init__( self, provCtx, name, rootUrl ): + self.provCtx = provCtx + self.name = name + self.rootUrl = rootUrl + + def getName( self ): + return self.name + + def getChildNodes( self ): + items = self.provCtx.mapPackageName2Path.items() + browseNodeList = [] + for i in items: + if len( i[1].paths ) == 1: + browseNodeList.append( + DirBrowseNode( self.provCtx, i[0], i[1].paths[0] )) + else: + for j in i[1].paths: + browseNodeList.append( + DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) ) + return tuple( browseNodeList ) + + def hasChildNodes( self ): + return len( self.provCtx.mapPackageName2Path ) > 0 + + def getType( self ): + return CONTAINER + + def getScript( self, uri ): + log.debug( "PackageBrowseNode getScript " + uri + " invoked" ) + raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 ) + + + + +class PythonScript( unohelper.Base, XScript ): + def __init__( self, func, mod, args ): + self.func = func + self.mod = mod + self.args = args + + def invoke(self, args, out, outindex ): + log.debug( "PythonScript.invoke " + str( args ) ) + try: + if (self.args): + args += self.args + ret = self.func( *args ) + except UnoException as e: + # UNO Exception continue to fly ... + text = lastException2String() + complete = "Error during invoking function " + \ + str(self.func.__name__) + " in module " + \ + self.mod.__file__ + " (" + text + ")" + log.debug( complete ) + # some people may beat me up for modifying the exception text, + # but otherwise office just shows + # the type name and message text with no more information, + # this is really bad for most users. + e.Message = e.Message + " (" + complete + ")" + raise + except Exception as e: + # General python exception are converted to uno RuntimeException + text = lastException2String() + complete = "Error during invoking function " + \ + str(self.func.__name__) + " in module " + \ + self.mod.__file__ + " (" + text + ")" + log.debug( complete ) + raise RuntimeException( complete , self ) + log.debug( "PythonScript.invoke ret = " + str( ret ) ) + return ret, (), () + +def expandUri( uri ): + if uri.startswith( "vnd.sun.star.expand:" ): + uri = uri.replace( "vnd.sun.star.expand:", "",1) + uri = uno.getComponentContext().getByName( + "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri ) + if uri.startswith( "file:" ): + uri = uno.absolutize("",uri) # necessary to get rid of .. in uri + return uri + +#-------------------------------------------------------------- +class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer): + def __init__( self, ctx, *args ): + if log.isDebugLevel(): + mystr = "" + for i in args: + if len(mystr) > 0: + mystr = mystr +"," + mystr = mystr + str(i) + log.debug( "Entering PythonScriptProvider.ctor" + mystr ) + + doc = None + inv = None + storageType = "" + + if isinstance(args[0], str): + storageType = args[0] + if storageType.startswith( "vnd.sun.star.tdoc" ): + doc = getModelFromDocUrl(ctx, storageType) + else: + inv = args[0] + try: + doc = inv.ScriptContainer + content = ctx.getServiceManager().createInstanceWithContext( + "com.sun.star.frame.TransientDocumentsDocumentContentFactory", + ctx).createDocumentContent(doc) + storageType = content.getIdentifier().getContentIdentifier() + except Exception as e: + text = lastException2String() + log.error( text ) + + isPackage = storageType.endswith( ":uno_packages" ) + + try: +# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext( +# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx) + urlHelper = MyUriHelper( ctx, storageType ) + log.debug( "got urlHelper " + str( urlHelper ) ) + + rootUrl = expandUri( urlHelper.getRootStorageURI() ) + log.debug( storageType + " transformed to " + rootUrl ) + + ucbService = "com.sun.star.ucb.SimpleFileAccess" + sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx ) + if not sfa: + log.debug("PythonScriptProvider couldn't instantiate " +ucbService) + raise RuntimeException( + "PythonScriptProvider couldn't instantiate " +ucbService, self) + self.provCtx = ProviderContext( + storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) ) + if isPackage: + mapPackageName2Path = getPackageName2PathMap( sfa, storageType ) + self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl ) + self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl ) + else: + self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl ) + + except Exception as e: + text = lastException2String() + log.debug( "PythonScriptProvider could not be instantiated because of : " + text ) + raise e + + def getName( self ): + return self.dirBrowseNode.getName() + + def getChildNodes( self ): + return self.dirBrowseNode.getChildNodes() + + def hasChildNodes( self ): + return self.dirBrowseNode.hasChildNodes() + + def getType( self ): + return self.dirBrowseNode.getType() + + # retrieve function args in parenthesis + def getFunctionArguments(self, func_signature): + nOpenParenthesis = func_signature.find( "(" ) + if -1 == nOpenParenthesis: + function_name = func_signature + arguments = None + else: + function_name = func_signature[0:nOpenParenthesis] + arg_part = func_signature[nOpenParenthesis+1:len(func_signature)] + nCloseParenthesis = arg_part.find( ")" ) + if -1 == nCloseParenthesis: + raise IllegalArgumentException( "PythonLoader: mismatch parenthesis " + func_signature, self, 0 ) + arguments = arg_part[0:nCloseParenthesis].strip() + if arguments == "": + arguments = None + else: + arguments = tuple([x.strip().strip('"') for x in arguments.split(",")]) + return function_name, arguments + + def getScript( self, scriptUri ): + try: + log.debug( "getScript " + scriptUri + " invoked") + + storageUri = self.provCtx.getStorageUrlFromPersistentUrl( + self.provCtx.uriHelper.getStorageURI(scriptUri) ); + log.debug( "getScript: storageUri = " + storageUri) + fileUri = storageUri[0:storageUri.find( "$" )] + funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)] + + # retrieve arguments in parenthesis + funcName, funcArgs = self.getFunctionArguments(funcName) + log.debug( " getScript : parsed funcname " + str(funcName) ) + log.debug( " getScript : func args " + str(funcArgs) ) + + mod = self.provCtx.getModuleByUrl( fileUri ) + log.debug( " got mod " + str(mod) ) + + func = mod.__dict__[ funcName ] + + log.debug( "got func " + str( func ) ) + return PythonScript( func, mod, funcArgs ) + except: + text = lastException2String() + log.error( text ) + raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 ) + + + # XServiceInfo + def getSupportedServices( self ): + return g_ImplementationHelper.getSupportedServices(g_implName) + + def supportsService( self, ServiceName ): + return g_ImplementationHelper.supportsService( g_implName, ServiceName ) + + def getImplementationName(self): + return g_implName + + def getByName( self, name ): + log.debug( "getByName called" + str( name )) + return None + + + def getElementNames( self ): + log.debug( "getElementNames called") + return () + + def hasByName( self, name ): + try: + log.debug( "hasByName called " + str( name )) + uri = expandUri(name) + ret = self.provCtx.isUrlInPackage( uri ) + log.debug( "hasByName " + uri + " " +str( ret ) ) + return ret + except: + text = lastException2String() + log.debug( "Error in hasByName:" + text ) + return False + + def removeByName( self, name ): + log.debug( "removeByName called" + str( name )) + uri = expandUri( name ) + if self.provCtx.isUrlInPackage( uri ): + self.provCtx.removePackageByUrl( uri ) + else: + log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" ) + raise NoSuchElementException( uri + "is not in package" , self ) + log.debug( "removeByName called" + str( uri ) + " successful" ) + + def insertByName( self, name, value ): + log.debug( "insertByName called " + str( name ) + " " + str( value )) + uri = expandUri( name ) + if isPyFileInPath( self.provCtx.sfa, uri ): + self.provCtx.addPackageByUrl( uri ) + else: + # package is no python package ... + log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" ) + raise IllegalArgumentException( uri + " does not contain .py files", self, 1 ) + log.debug( "insertByName called " + str( uri ) + " successful" ) + + def replaceByName( self, name, value ): + log.debug( "replaceByName called " + str( name ) + " " + str( value )) + uri = expandUri( name ) + self.removeByName( name ) + self.insertByName( name, value ) + log.debug( "replaceByName called" + str( uri ) + " successful" ) + + def getElementType( self ): + log.debug( "getElementType called" ) + return uno.getTypeByName( "void" ) + + def hasElements( self ): + log.debug( "hasElements got called") + return False + +g_ImplementationHelper.addImplementation( \ + PythonScriptProvider,g_implName, \ + ("com.sun.star.script.provider.LanguageScriptProvider", + "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),) + + +log.debug( "pythonscript finished initializing" ) + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/scripting/source/pyprov/scriptproviderforpython.rdb b/scripting/source/pyprov/scriptproviderforpython.rdb new file mode 100644 index 000000000..09a37e4da --- /dev/null +++ b/scripting/source/pyprov/scriptproviderforpython.rdb @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<!-- + * 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 . +--> +<components xmlns="http://openoffice.org/2010/uno-components"> + <component loader="com.sun.star.loader.Python" + uri="vnd.openoffice.pymodule:pythonscript"> + <implementation + name="org.libreoffice.pyuno.LanguageScriptProviderForPython"> + <service name="com.sun.star.script.provider.ScriptProviderForPython"/> + <service name="com.sun.star.script.provider.LanguageScriptProvider"/> + </implementation> + </component> +</components> diff --git a/scripting/source/stringresource/stringresource.component b/scripting/source/stringresource/stringresource.component new file mode 100644 index 000000000..a2b2a24f3 --- /dev/null +++ b/scripting/source/stringresource/stringresource.component @@ -0,0 +1,34 @@ +<?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.scripting.StringResource" + constructor="scripting_StringResourcePersistenceImpl_implementation"> + <service name="com.sun.star.resource.StringResource"/> + </implementation> + <implementation name="com.sun.star.comp.scripting.StringResourceWithLocation" + constructor="scripting_StringResourceWithLocationImpl_get_implementation"> + <service name="com.sun.star.resource.StringResourceWithLocation"/> + </implementation> + <implementation name="com.sun.star.comp.scripting.StringResourceWithStorage" + constructor="scripting_StringResourceWithStorageImpl_get_implementation"> + <service name="com.sun.star.resource.StringResourceWithStorage"/> + </implementation> +</component> diff --git a/scripting/source/stringresource/stringresource.cxx b/scripting/source/stringresource/stringresource.cxx new file mode 100644 index 000000000..06b52b037 --- /dev/null +++ b/scripting/source/stringresource/stringresource.cxx @@ -0,0 +1,2598 @@ +/* -*- 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 <memory> +#include "stringresource.hxx" +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/TextInputStream.hpp> +#include <com/sun/star/io/TextOutputStream.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/resource/MissingResourceException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> +#include <rtl/ustrbuf.hxx> +#include <tools/urlobj.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; + + +namespace stringresource +{ + +// StringResourceImpl + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_StringResourcePersistenceImpl_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new StringResourcePersistenceImpl(context)); +} + + +StringResourceImpl::StringResourceImpl( const Reference< XComponentContext >& rxContext ) + : m_xContext( rxContext ) + , m_pCurrentLocaleItem( nullptr ) + , m_pDefaultLocaleItem( nullptr ) + , m_bDefaultModified( false ) + , m_bModified( false ) + , m_bReadOnly( false ) + , m_nNextUniqueNumericId( UNIQUE_NUMBER_NEEDS_INITIALISATION ) +{ +} + + +StringResourceImpl::~StringResourceImpl() +{ +} + + +// XServiceInfo + +OUString StringResourceImpl::getImplementationName( ) +{ + return "com.sun.star.comp.scripting.StringResource"; +} + +sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > StringResourceImpl::getSupportedServiceNames( ) +{ + return { "com.sun.star.resource.StringResource" }; +} + + +// XModifyBroadcaster + +void StringResourceImpl::addModifyListener( const Reference< XModifyListener >& aListener ) +{ + if( !aListener.is() ) + throw RuntimeException(); + + std::unique_lock aGuard( m_aMutex ); + m_aListenerContainer.addInterface( aGuard, aListener ); +} + +void StringResourceImpl::removeModifyListener( const Reference< XModifyListener >& aListener ) +{ + if( !aListener.is() ) + throw RuntimeException(); + + std::unique_lock aGuard( m_aMutex ); + m_aListenerContainer.removeInterface( aGuard, aListener ); +} + + +// XStringResourceResolver + +OUString StringResourceImpl::implResolveString + ( const OUString& ResourceID, LocaleItem* pLocaleItem ) +{ + OUString aRetStr; + bool bSuccess = false; + if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) ) + { + IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID ); + if( it != pLocaleItem->m_aIdToStringMap.end() ) + { + aRetStr = (*it).second; + bSuccess = true; + } + } + if( !bSuccess ) + { + throw css::resource::MissingResourceException( "StringResourceImpl: No entry for ResourceID: " + ResourceID ); + } + return aRetStr; +} + +OUString StringResourceImpl::resolveString( const OUString& ResourceID ) +{ + std::unique_lock aGuard( m_aMutex ); + return implResolveString( ResourceID, m_pCurrentLocaleItem ); +} + +OUString StringResourceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + LocaleItem* pLocaleItem = getItemForLocale( locale, false ); + return implResolveString( ResourceID, pLocaleItem ); +} + +bool StringResourceImpl::implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem ) +{ + bool bSuccess = false; + if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) ) + { + IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID ); + if( it != pLocaleItem->m_aIdToStringMap.end() ) + bSuccess = true; + } + return bSuccess; +} + +sal_Bool StringResourceImpl::hasEntryForId( const OUString& ResourceID ) +{ + std::unique_lock aGuard( m_aMutex ); + return implHasEntryForId( ResourceID, m_pCurrentLocaleItem ); +} + +sal_Bool StringResourceImpl::hasEntryForIdAndLocale( const OUString& ResourceID, + const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + LocaleItem* pLocaleItem = getItemForLocale( locale, false ); + return implHasEntryForId( ResourceID, pLocaleItem ); +} + +Sequence< OUString > StringResourceImpl::implGetResourceIDs( LocaleItem* pLocaleItem ) +{ + Sequence< OUString > aIDSeq( 0 ); + if( pLocaleItem && loadLocale( pLocaleItem ) ) + { + const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap; + sal_Int32 nResourceIDCount = rHashMap.size(); + aIDSeq.realloc( nResourceIDCount ); + OUString* pStrings = aIDSeq.getArray(); + + int iTarget = 0; + for( const auto& rEntry : rHashMap ) + { + OUString aStr = rEntry.first; + pStrings[iTarget] = aStr; + iTarget++; + } + } + return aIDSeq; +} + +Sequence< OUString > StringResourceImpl::getResourceIDsForLocale + ( const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + LocaleItem* pLocaleItem = getItemForLocale( locale, false ); + return implGetResourceIDs( pLocaleItem ); +} + +Sequence< OUString > StringResourceImpl::getResourceIDs( ) +{ + std::unique_lock aGuard( m_aMutex ); + return implGetResourceIDs( m_pCurrentLocaleItem ); +} + +Locale StringResourceImpl::getCurrentLocale() +{ + std::unique_lock aGuard( m_aMutex ); + + Locale aRetLocale; + if( m_pCurrentLocaleItem != nullptr ) + aRetLocale = m_pCurrentLocaleItem->m_locale; + return aRetLocale; +} + +Locale StringResourceImpl::getDefaultLocale( ) +{ + std::unique_lock aGuard( m_aMutex ); + + Locale aRetLocale; + if( m_pDefaultLocaleItem != nullptr ) + aRetLocale = m_pDefaultLocaleItem->m_locale; + return aRetLocale; +} + +Sequence< Locale > StringResourceImpl::getLocales( ) +{ + std::unique_lock aGuard( m_aMutex ); + + sal_Int32 nSize = m_aLocaleItemVector.size(); + Sequence< Locale > aLocalSeq( nSize ); + Locale* pLocales = aLocalSeq.getArray(); + int iTarget = 0; + for( const auto& pLocaleItem : m_aLocaleItemVector ) + { + pLocales[iTarget] = pLocaleItem->m_locale; + iTarget++; + } + return aLocalSeq; +} + + +// XStringResourceManager + +void StringResourceImpl::implCheckReadOnly( const char* pExceptionMsg ) +{ + if( m_bReadOnly ) + { + OUString errorMsg = OUString::createFromAscii( pExceptionMsg ); + throw NoSupportException( errorMsg ); + } +} + +sal_Bool StringResourceImpl::isReadOnly() +{ + return m_bReadOnly; +} + +void StringResourceImpl::implSetCurrentLocale( std::unique_lock<std::mutex>& rGuard, const Locale& locale, + bool FindClosestMatch, bool bUseDefaultIfNoMatch ) +{ + LocaleItem* pLocaleItem = nullptr; + if( FindClosestMatch ) + pLocaleItem = getClosestMatchItemForLocale( locale ); + else + pLocaleItem = getItemForLocale( locale, true ); + + if( pLocaleItem == nullptr && bUseDefaultIfNoMatch ) + pLocaleItem = m_pDefaultLocaleItem; + + if( pLocaleItem != nullptr ) + { + (void)loadLocale( pLocaleItem ); + m_pCurrentLocaleItem = pLocaleItem; + + // Only notify without modifying + implNotifyListeners(rGuard); + } +} + +void StringResourceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch ) +{ + std::unique_lock aGuard( m_aMutex ); + implSetCurrentLocale( aGuard, locale, FindClosestMatch, false/*bUseDefaultIfNoMatch*/ ); +} + +void StringResourceImpl::setDefaultLocale( const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::setDefaultLocale(): Read only" ); + + LocaleItem* pLocaleItem = getItemForLocale( locale, true ); + if( pLocaleItem && pLocaleItem != m_pDefaultLocaleItem ) + { + if( m_pDefaultLocaleItem ) + { + m_aChangedDefaultLocaleVector.push_back( + std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) ); + } + + m_pDefaultLocaleItem = pLocaleItem; + m_bDefaultModified = true; + implModified(aGuard); + } +} + +void StringResourceImpl::implSetString( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, + const OUString& Str, LocaleItem* pLocaleItem ) +{ + if( !(pLocaleItem != nullptr && loadLocale( pLocaleItem )) ) + return; + + IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap; + + IdToStringMap::iterator it = rHashMap.find( ResourceID ); + bool bNew = ( it == rHashMap.end() ); + if( bNew ) + { + IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap; + rIndexMap[ ResourceID ] = pLocaleItem->m_nNextIndex++; + implScanIdForNumber( ResourceID ); + } + rHashMap[ ResourceID ] = Str; + pLocaleItem->m_bModified = true; + implModified(rGuard); +} + +void StringResourceImpl::setString( const OUString& ResourceID, const OUString& Str ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::setString(): Read only" ); + implSetString( aGuard, ResourceID, Str, m_pCurrentLocaleItem ); +} + +void StringResourceImpl::setStringForLocale + ( const OUString& ResourceID, const OUString& Str, const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::setStringForLocale(): Read only" ); + LocaleItem* pLocaleItem = getItemForLocale( locale, false ); + implSetString( aGuard, ResourceID, Str, pLocaleItem ); +} + +void StringResourceImpl::implRemoveId( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem ) +{ + if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) ) + { + IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap; + IdToStringMap::iterator it = rHashMap.find( ResourceID ); + if( it == rHashMap.end() ) + { + throw css::resource::MissingResourceException( "StringResourceImpl: No entries for ResourceID: " + ResourceID ); + } + rHashMap.erase( it ); + pLocaleItem->m_bModified = true; + implModified(rGuard); + } +} + +void StringResourceImpl::removeId( const OUString& ResourceID ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::removeId(): Read only" ); + implRemoveId( aGuard, ResourceID, m_pCurrentLocaleItem ); +} + +void StringResourceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::removeIdForLocale(): Read only" ); + LocaleItem* pLocaleItem = getItemForLocale( locale, false ); + implRemoveId( aGuard, ResourceID, pLocaleItem ); +} + +void StringResourceImpl::newLocale( const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::newLocale(): Read only" ); + + if( getItemForLocale( locale, false ) != nullptr ) + { + throw ElementExistException( "StringResourceImpl: locale already exists" ); + } + + // TODO?: Check if locale is valid? How? + //if (!bValid) + //{ + // OUString errorMsg("StringResourceImpl: Invalid locale"); + // throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 0 ); + //} + + LocaleItem* pLocaleItem = new LocaleItem( locale ); + m_aLocaleItemVector.emplace_back( pLocaleItem ); + pLocaleItem->m_bModified = true; + + // Copy strings from default locale + LocaleItem* pCopyFromItem = m_pDefaultLocaleItem; + if( pCopyFromItem == nullptr ) + pCopyFromItem = m_pCurrentLocaleItem; + if( pCopyFromItem != nullptr && loadLocale( pCopyFromItem ) ) + { + const IdToStringMap& rSourceMap = pCopyFromItem->m_aIdToStringMap; + IdToStringMap& rTargetMap = pLocaleItem->m_aIdToStringMap; + for( const auto& rEntry : rSourceMap ) + { + OUString aId = rEntry.first; + OUString aStr = rEntry.second; + rTargetMap[ aId ] = aStr; + } + + const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap; + IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap; + for( const auto& rIndex : rSourceIndexMap ) + { + OUString aId = rIndex.first; + sal_Int32 nIndex = rIndex.second; + rTargetIndexMap[ aId ] = nIndex; + } + pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex; + } + + if( m_pCurrentLocaleItem == nullptr ) + m_pCurrentLocaleItem = pLocaleItem; + + if( m_pDefaultLocaleItem == nullptr ) + { + m_pDefaultLocaleItem = pLocaleItem; + m_bDefaultModified = true; + } + + implModified(aGuard); +} + +void StringResourceImpl::removeLocale( const Locale& locale ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" ); + + LocaleItem* pRemoveItem = getItemForLocale( locale, true ); + if( !pRemoveItem ) + return; + + // Last locale? + sal_Int32 nLocaleCount = m_aLocaleItemVector.size(); + if( nLocaleCount > 1 ) + { + if( m_pCurrentLocaleItem == pRemoveItem || + m_pDefaultLocaleItem == pRemoveItem ) + { + LocaleItem* pFallbackItem = nullptr; + for( const auto& pLocaleItem : m_aLocaleItemVector ) + { + if( pLocaleItem.get() != pRemoveItem ) + { + pFallbackItem = pLocaleItem.get(); + break; + } + } + if( m_pCurrentLocaleItem == pRemoveItem ) + { + setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ ); + } + if( m_pDefaultLocaleItem == pRemoveItem ) + { + setDefaultLocale( pFallbackItem->m_locale ); + } + } + } + auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(), + [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; }); + if (it == m_aLocaleItemVector.end()) + return; + + // Remember locale item to delete file while storing + m_aDeletedLocaleItemVector.push_back( std::move(*it) ); + + // Last locale? + if( nLocaleCount == 1 ) + { + m_nNextUniqueNumericId = 0; + if( m_pDefaultLocaleItem ) + { + m_aChangedDefaultLocaleVector.push_back( + std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) ); + } + m_pCurrentLocaleItem = nullptr; + m_pDefaultLocaleItem = nullptr; + } + + m_aLocaleItemVector.erase( it ); + + implModified(aGuard); +} + +void StringResourceImpl::implScanIdForNumber( const OUString& ResourceID ) +{ + const sal_Unicode* pSrc = ResourceID.getStr(); + sal_Int32 nLen = ResourceID.getLength(); + + sal_Int32 nNumber = 0; + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + sal_Unicode c = pSrc[i]; + if( c >= '0' && c <= '9' ) + { + sal_uInt16 nDigitVal = c - '0'; + nNumber = 10*nNumber + nDigitVal; + } + else + break; + } + + if( m_nNextUniqueNumericId < nNumber + 1 ) + m_nNextUniqueNumericId = nNumber + 1; +} + +sal_Int32 StringResourceImpl::getUniqueNumericId( ) +{ + if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION ) + { + implLoadAllLocales(); + m_nNextUniqueNumericId = 0; + } + + if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION ) + { + throw NoSupportException( "getUniqueNumericId: Extended sal_Int32 range" ); + } + return m_nNextUniqueNumericId; +} + + +// Private helper methods + +LocaleItem* StringResourceImpl::getItemForLocale + ( const Locale& locale, bool bException ) +{ + LocaleItem* pRetItem = nullptr; + + // Search for locale + for( auto& pLocaleItem : m_aLocaleItemVector ) + { + if( pLocaleItem ) + { + Locale& cmp_locale = pLocaleItem->m_locale; + if( cmp_locale.Language == locale.Language && + cmp_locale.Country == locale.Country && + cmp_locale.Variant == locale.Variant ) + { + pRetItem = pLocaleItem.get(); + break; + } + } + } + + if( pRetItem == nullptr && bException ) + { + throw IllegalArgumentException( "StringResourceImpl: Invalid locale", Reference< XInterface >(), 0 ); + } + return pRetItem; +} + +// Returns the LocaleItem for a given locale, if it exists, otherwise NULL. +// This method performs a closest match search, at least the language must match. +LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale ) +{ + LocaleItem* pRetItem = nullptr; + + ::std::vector< Locale > aLocales( m_aLocaleItemVector.size()); + size_t i = 0; + for( const auto& pLocaleItem : m_aLocaleItemVector ) + { + aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale()); + ++i; + } + ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale)); + if (iFound != aLocales.end()) + pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get(); + + return pRetItem; +} + +void StringResourceImpl::implModified(std::unique_lock<std::mutex>& rGuard) +{ + m_bModified = true; + implNotifyListeners(rGuard); +} + +void StringResourceImpl::implNotifyListeners(std::unique_lock<std::mutex>& rGuard) +{ + EventObject aEvent; + aEvent.Source = static_cast< XInterface* >( static_cast<OWeakObject*>(this) ); + m_aListenerContainer.forEach(rGuard, + [&aEvent](const css::uno::Reference<XModifyListener>& xListener) + { + xListener->modified(aEvent); + } + ); +} + + +// Loading + +bool StringResourceImpl::loadLocale( LocaleItem* ) +{ + // Base implementation has nothing to load + return true; +} + +void StringResourceImpl::implLoadAllLocales() +{ + // Base implementation has nothing to load +} + + +// StringResourcePersistenceImpl + + +StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext ) + : StringResourcePersistenceImpl_BASE( rxContext ) +{ +} + + +StringResourcePersistenceImpl::~StringResourcePersistenceImpl() +{ +} + + +// XServiceInfo + + +OUString StringResourcePersistenceImpl::getImplementationName( ) +{ + return "com.sun.star.comp.scripting.StringResource"; +} + + +sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService( this, rServiceName ); +} + + +Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames( ) +{ + return StringResourceImpl::getSupportedServiceNames(); +} + + +// XInitialization base functionality for derived classes + + +constexpr OUStringLiteral aNameBaseDefaultStr = u"strings"; + +void StringResourcePersistenceImpl::implInitializeCommonParameters + ( std::unique_lock<std::mutex>& rGuard, const Sequence< Any >& aArguments ) +{ + bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly); + if( !bReadOnlyOk ) + { + throw IllegalArgumentException( "XInitialization::initialize: Expected ReadOnly flag", Reference< XInterface >(), 1 ); + } + + css::lang::Locale aCurrentLocale; + bool bLocaleOk = (aArguments[2] >>= aCurrentLocale); + if( !bLocaleOk ) + { + throw IllegalArgumentException( "XInitialization::initialize: Expected Locale", Reference< XInterface >(), 2 ); + } + + bool bNameBaseOk = (aArguments[3] >>= m_aNameBase); + if( !bNameBaseOk ) + { + throw IllegalArgumentException( "XInitialization::initialize: Expected NameBase string", Reference< XInterface >(), 3 ); + } + if( m_aNameBase.isEmpty() ) + m_aNameBase = aNameBaseDefaultStr; + + bool bCommentOk = (aArguments[4] >>= m_aComment); + if( !bCommentOk ) + { + throw IllegalArgumentException( "XInitialization::initialize: Expected Comment string", Reference< XInterface >(), 4 ); + } + + implScanLocales(); + + implSetCurrentLocale( rGuard, aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ ); +} + + +// Forwarding calls to base class + +// XModifyBroadcaster +void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener ) +{ + StringResourceImpl::addModifyListener( aListener ); +} +void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener ) +{ + StringResourceImpl::removeModifyListener( aListener ); +} + +// XStringResourceResolver +OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID ) +{ + return StringResourceImpl::resolveString( ResourceID ) ; +} +OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale ) +{ + return StringResourceImpl::resolveStringForLocale( ResourceID, locale ); +} +sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID ) +{ + return StringResourceImpl::hasEntryForId( ResourceID ) ; +} +sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID, + const Locale& locale ) +{ + return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale ); +} +Locale StringResourcePersistenceImpl::getCurrentLocale() +{ + return StringResourceImpl::getCurrentLocale(); +} +Locale StringResourcePersistenceImpl::getDefaultLocale( ) +{ + return StringResourceImpl::getDefaultLocale(); +} +Sequence< Locale > StringResourcePersistenceImpl::getLocales( ) +{ + return StringResourceImpl::getLocales(); +} + +// XStringResourceManager +sal_Bool StringResourcePersistenceImpl::isReadOnly() +{ + return StringResourceImpl::isReadOnly(); +} +void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch ) +{ + StringResourceImpl::setCurrentLocale( locale, FindClosestMatch ); +} +void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale ) +{ + StringResourceImpl::setDefaultLocale( locale ); +} +Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs( ) +{ + return StringResourceImpl::getResourceIDs(); +} +void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str ) +{ + StringResourceImpl::setString( ResourceID, Str ); +} +void StringResourcePersistenceImpl::setStringForLocale + ( const OUString& ResourceID, const OUString& Str, const Locale& locale ) +{ + StringResourceImpl::setStringForLocale( ResourceID, Str, locale ); +} +Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale + ( const Locale& locale ) +{ + return StringResourceImpl::getResourceIDsForLocale( locale ); +} +void StringResourcePersistenceImpl::removeId( const OUString& ResourceID ) +{ + StringResourceImpl::removeId( ResourceID ); +} +void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale ) +{ + StringResourceImpl::removeIdForLocale( ResourceID, locale ); +} +void StringResourcePersistenceImpl::newLocale( const Locale& locale ) +{ + StringResourceImpl::newLocale( locale ); +} +void StringResourcePersistenceImpl::removeLocale( const Locale& locale ) +{ + StringResourceImpl::removeLocale( locale ); +} +sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId( ) +{ + return StringResourceImpl::getUniqueNumericId(); +} + + +// XStringResourcePersistence + +void StringResourcePersistenceImpl::store() +{ +} + +sal_Bool StringResourcePersistenceImpl::isModified( ) +{ + std::unique_lock aGuard( m_aMutex ); + + return m_bModified; +} + +void StringResourcePersistenceImpl::setComment( const OUString& Comment ) +{ + m_aComment = Comment; +} + +void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage, + const OUString& NameBase, const OUString& Comment ) +{ + std::unique_lock aGuard( m_aMutex ); + + implStoreAtStorage( NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ ); +} + +void StringResourcePersistenceImpl::implStoreAtStorage +( + const OUString& aNameBase, + const OUString& aComment, + const Reference< css::embed::XStorage >& Storage, + bool bUsedForStore, + bool bStoreAll +) +{ + // Delete files for deleted locales + if( bUsedForStore ) + { + for( auto& pLocaleItem : m_aDeletedLocaleItemVector ) + { + if( pLocaleItem ) + { + OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties"; + + try + { + Storage->removeElement( aStreamName ); + } + catch( Exception& ) + {} + + pLocaleItem.reset(); + } + } + m_aDeletedLocaleItemVector.clear(); + } + + for( auto& pLocaleItem : m_aLocaleItemVector ) + { + if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) && + loadLocale( pLocaleItem.get() ) ) + { + OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties"; + + Reference< io::XStream > xElementStream = + Storage->openStreamElement( aStreamName, ElementModes::READWRITE ); + + uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY ); + OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" ); + if ( xProps.is() ) + { + OUString aPropName("MediaType"); + xProps->setPropertyValue( aPropName, uno::Any( OUString("text/plain") ) ); + + aPropName = "UseCommonStoragePasswordEncryption"; + xProps->setPropertyValue( aPropName, uno::Any( true ) ); + } + + Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream(); + if( xOutputStream.is() ) + implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment ); + xOutputStream->closeOutput(); + + if( bUsedForStore ) + pLocaleItem->m_bModified = false; + } + } + + // Delete files for changed defaults + if( bUsedForStore ) + { + for( auto& pLocaleItem : m_aChangedDefaultLocaleVector ) + { + OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default"; + + try + { + Storage->removeElement( aStreamName ); + } + catch( Exception& ) + {} + + pLocaleItem.reset(); + } + m_aChangedDefaultLocaleVector.clear(); + } + + // Default locale + if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) ) + return; + + OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default"; + + Reference< io::XStream > xElementStream = + Storage->openStreamElement( aStreamName, ElementModes::READWRITE ); + + // Only create stream without content + Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream(); + xOutputStream->closeOutput(); + + if( bUsedForStore ) + m_bDefaultModified = false; +} + +void StringResourcePersistenceImpl::storeToURL( const OUString& URL, + const OUString& NameBase, const OUString& Comment, + const Reference< css::task::XInteractionHandler >& Handler ) +{ + std::unique_lock aGuard( m_aMutex ); + + Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext); + if( xFileAccess.is() && Handler.is() ) + xFileAccess->setInteractionHandler( Handler ); + + implStoreAtLocation( URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/ ); +} + +void StringResourcePersistenceImpl::implKillRemovedLocaleFiles +( + std::u16string_view Location, + const OUString& aNameBase, + const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess +) +{ + // Delete files for deleted locales + for( auto& pLocaleItem : m_aDeletedLocaleItemVector ) + { + if( pLocaleItem ) + { + OUString aCompleteFileName = + implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location ); + if( xFileAccess->exists( aCompleteFileName ) ) + xFileAccess->kill( aCompleteFileName ); + + pLocaleItem.reset(); + } + } + m_aDeletedLocaleItemVector.clear(); +} + +void StringResourcePersistenceImpl::implKillChangedDefaultFiles +( + std::u16string_view Location, + const OUString& aNameBase, + const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess +) +{ + // Delete files for changed defaults + for( auto& pLocaleItem : m_aChangedDefaultLocaleVector ) + { + OUString aCompleteFileName = + implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true ); + if( xFileAccess->exists( aCompleteFileName ) ) + xFileAccess->kill( aCompleteFileName ); + pLocaleItem.reset(); + } + m_aChangedDefaultLocaleVector.clear(); +} + +void StringResourcePersistenceImpl::implStoreAtLocation +( + std::u16string_view Location, + const OUString& aNameBase, + const OUString& aComment, + const Reference< ucb::XSimpleFileAccess3 >& xFileAccess, + bool bUsedForStore, + bool bStoreAll, + bool bKillAll +) +{ + // Delete files for deleted locales + if( bUsedForStore || bKillAll ) + implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess ); + + for( auto& pLocaleItem : m_aLocaleItemVector ) + { + if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) && + loadLocale( pLocaleItem.get() ) ) + { + OUString aCompleteFileName = + implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location ); + if( xFileAccess->exists( aCompleteFileName ) ) + xFileAccess->kill( aCompleteFileName ); + + if( !bKillAll ) + { + // Create Output stream + Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName ); + if( xOutputStream.is() ) + { + implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment ); + xOutputStream->closeOutput(); + } + if( bUsedForStore ) + pLocaleItem->m_bModified = false; + } + } + } + + // Delete files for changed defaults + if( bUsedForStore || bKillAll ) + implKillChangedDefaultFiles( Location, aNameBase, xFileAccess ); + + // Default locale + if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) ) + return; + + OUString aCompleteFileName = + implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true ); + if( xFileAccess->exists( aCompleteFileName ) ) + xFileAccess->kill( aCompleteFileName ); + + if( !bKillAll ) + { + // Create Output stream + Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName ); + if( xOutputStream.is() ) + xOutputStream->closeOutput(); + + if( bUsedForStore ) + m_bDefaultModified = false; + } +} + + +// BinaryOutput, helper class for exportBinary + +class BinaryOutput +{ + Reference< XComponentContext > m_xContext; + Reference< XInterface > m_xTempFile; + Reference< io::XOutputStream > m_xOutputStream; + +public: + explicit BinaryOutput( Reference< XComponentContext > const & xContext ); + + const Reference< io::XOutputStream >& getOutputStream() const + { return m_xOutputStream; } + + Sequence< ::sal_Int8 > closeAndGetData(); + + // Template to be used with sal_Int16 and sal_Unicode + template< class T > + void write16BitInt( T n ); + void writeInt16( sal_Int16 n ) + { write16BitInt( n ); } + void writeUnicodeChar( sal_Unicode n ) + { write16BitInt( n ); } + void writeInt32( sal_Int32 n ); + void writeString( const OUString& aStr ); +}; + +BinaryOutput::BinaryOutput( Reference< XComponentContext > const & xContext ) + : m_xContext( xContext ) +{ + m_xTempFile = io::TempFile::create( m_xContext ); + m_xOutputStream.set( m_xTempFile, UNO_QUERY_THROW ); +} + +template< class T > +void BinaryOutput::write16BitInt( T n ) +{ + if( !m_xOutputStream.is() ) + return; + + Sequence< sal_Int8 > aSeq( 2 ); + sal_Int8* p = aSeq.getArray(); + + sal_Int8 nLow = sal_Int8( n & 0xff ); + sal_Int8 nHigh = sal_Int8( n >> 8 ); + + p[0] = nLow; + p[1] = nHigh; + m_xOutputStream->writeBytes( aSeq ); +} + +void BinaryOutput::writeInt32( sal_Int32 n ) +{ + if( !m_xOutputStream.is() ) + return; + + Sequence< sal_Int8 > aSeq( 4 ); + sal_Int8* p = aSeq.getArray(); + + for( sal_Int16 i = 0 ; i < 4 ; i++ ) + { + p[i] = sal_Int8( n & 0xff ); + n >>= 8; + } + m_xOutputStream->writeBytes( aSeq ); +} + +void BinaryOutput::writeString( const OUString& aStr ) +{ + sal_Int32 nLen = aStr.getLength(); + const sal_Unicode* pStr = aStr.getStr(); + + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + writeUnicodeChar( pStr[i] ); + + writeUnicodeChar( 0 ); +} + +Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData() +{ + Sequence< ::sal_Int8 > aRetSeq; + if( !m_xOutputStream.is() ) + return aRetSeq; + + m_xOutputStream->closeOutput(); + + Reference< io::XSeekable> xSeekable( m_xTempFile, UNO_QUERY ); + if( !xSeekable.is() ) + return aRetSeq; + + sal_Int32 nSize = static_cast<sal_Int32>(xSeekable->getPosition()); + + Reference< io::XInputStream> xInputStream( m_xTempFile, UNO_QUERY ); + if( !xInputStream.is() ) + return aRetSeq; + + xSeekable->seek( 0 ); + sal_Int32 nRead = xInputStream->readBytes( aRetSeq, nSize ); + OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" ); + + return aRetSeq; +} + + +// Binary format: + +// Header +// Byte Content +// 0 + 1 sal_Int16: Version, currently 0, low byte first +// 2 + 3 sal_Int16: Locale count = n, low byte first +// 4 + 5 sal_Int16: Default Locale position in Locale list, == n if none +// 6 - 7 sal_Int32: Start index locale block 0, lowest byte first +// (n-1) * sal_Int32: Start index locale block 1 to n, lowest byte first +// 6 + 4*n sal_Int32: "Start index" non existing locale block n+1, +// marks the first invalid index, kind of EOF + +// Locale block +// All strings are stored as 2-Byte-0 terminated sequence +// of 16 bit Unicode characters, each with low byte first +// Empty strings only contain the 2-Byte-0 + +// Members of com.sun.star.lang.Locale +// with l1 = Locale.Language.getLength() +// with l2 = Locale.Country.getLength() +// with l3 = Locale.Variant.getLength() +// pos0 = 0 Locale.Language +// pos1 = 2 * (l1 + 1) Locale.Country +// pos2 = pos1 + 2 * (l2 + 1) Locale.Variant +// pos3 = pos2 + 2 * (l3 + 1) +// pos3 Properties file written by implWritePropertiesFile + +Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary( ) +{ + BinaryOutput aOut( m_xContext ); + + sal_Int32 nLocaleCount = m_aLocaleItemVector.size(); + std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount); + + sal_Int32 iLocale = 0; + sal_Int32 iDefault = 0; + for( auto& pLocaleItem : m_aLocaleItemVector ) + { + if( pLocaleItem != nullptr && loadLocale( pLocaleItem.get() ) ) + { + if( m_pDefaultLocaleItem == pLocaleItem.get() ) + iDefault = iLocale; + + BinaryOutput aLocaleOut( m_xContext ); + implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut ); + + aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData(); + } + ++iLocale; + } + + // Write header + sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount); + sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault); + aOut.writeInt16( 0 ); // nVersion + aOut.writeInt16( nLocaleCount16 ); + aOut.writeInt16( iDefault16 ); + + // Write data positions + sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1); + for( iLocale = 0; iLocale < nLocaleCount; iLocale++ ) + { + aOut.writeInt32( nDataPos ); + + Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale]; + sal_Int32 nSeqLen = rSeq.getLength(); + nDataPos += nSeqLen; + } + // Write final position + aOut.writeInt32( nDataPos ); + + // Write data + Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream(); + if( xOutputStream.is() ) + { + for( iLocale = 0; iLocale < nLocaleCount; iLocale++ ) + { + Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale]; + xOutputStream->writeBytes( rSeq ); + } + } + + Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData(); + return aRetSeq; +} + +void StringResourcePersistenceImpl::implWriteLocaleBinary + ( LocaleItem* pLocaleItem, BinaryOutput& rOut ) +{ + Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream(); + if( !xOutputStream.is() ) + return; + + Locale& rLocale = pLocaleItem->m_locale; + rOut.writeString( rLocale.Language ); + rOut.writeString( rLocale.Country ); + rOut.writeString( rLocale.Variant ); + implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment ); +} + + +// BinaryOutput, helper class for exportBinary + +namespace { + +class BinaryInput +{ + Sequence< sal_Int8 > m_aData; + Reference< XComponentContext > m_xContext; + + const sal_Int8* m_pData; + sal_Int32 m_nCurPos; + sal_Int32 m_nSize; + +public: + BinaryInput( const Sequence< ::sal_Int8 >& aData, Reference< XComponentContext > const & xContext ); + + Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize ); + + void seek( sal_Int32 nPos ); + sal_Int32 getPosition() const + { return m_nCurPos; } + + sal_Int16 readInt16(); + sal_Int32 readInt32(); + sal_Unicode readUnicodeChar(); + OUString readString(); +}; + +} + +BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData, Reference< XComponentContext > const & xContext ) + : m_aData( aData ) + , m_xContext( xContext ) +{ + m_pData = m_aData.getConstArray(); + m_nCurPos = 0; + m_nSize = m_aData.getLength(); +} + +Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize ) +{ + Reference< io::XInputStream > xIn; + if( m_nCurPos + nSize <= m_nSize ) + { + Reference< io::XOutputStream > xTempOut( io::TempFile::create(m_xContext), UNO_QUERY_THROW ); + Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize ); + xTempOut->writeBytes( aSection ); + + Reference< io::XSeekable> xSeekable( xTempOut, UNO_QUERY ); + if( xSeekable.is() ) + xSeekable->seek( 0 ); + + xIn.set( xTempOut, UNO_QUERY ); + } + else + OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" ); + + return xIn; +} + +void BinaryInput::seek( sal_Int32 nPos ) +{ + if( nPos <= m_nSize ) + m_nCurPos = nPos; + else + OSL_FAIL( "BinaryInput::seek(): Position past end" ); +} + + +sal_Int16 BinaryInput::readInt16() +{ + sal_Int16 nRet = 0; + if( m_nCurPos + 2 <= m_nSize ) + { + nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) ); + nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) ); + } + else + OSL_FAIL( "BinaryInput::readInt16(): Read past end" ); + + return nRet; +} + +sal_Int32 BinaryInput::readInt32() +{ + sal_Int32 nRet = 0; + if( m_nCurPos + 4 <= m_nSize ) + { + sal_Int32 nFactor = 1; + for( sal_Int16 i = 0; i < 4; i++ ) + { + nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor; + nFactor *= 256; + } + } + else + OSL_FAIL( "BinaryInput::readInt32(): Read past end" ); + + return nRet; +} + +sal_Unicode BinaryInput::readUnicodeChar() +{ + sal_uInt16 nRet = 0; + if( m_nCurPos + 2 <= m_nSize ) + { + nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] ); + nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] ); + } + else + OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" ); + + sal_Unicode cRet = nRet; + return cRet; +} + +OUString BinaryInput::readString() +{ + OUStringBuffer aBuf; + sal_Unicode c; + do + { + c = readUnicodeChar(); + if( c != 0 ) + aBuf.append( c ); + } + while( c != 0 ); + + OUString aRetStr = aBuf.makeStringAndClear(); + return aRetStr; +} + +void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data ) +{ + // Init: Remove all locales + sal_Int32 nOldLocaleCount = 0; + do + { + Sequence< Locale > aLocaleSeq = getLocales(); + nOldLocaleCount = aLocaleSeq.getLength(); + if( nOldLocaleCount > 0 ) + { + Locale aLocale = aLocaleSeq[0]; + removeLocale( aLocale ); + } + } + while( nOldLocaleCount > 0 ); + + // Import data + BinaryInput aIn( Data, m_xContext ); + + aIn.readInt16(); // version + sal_Int32 nLocaleCount = aIn.readInt16(); + sal_Int32 iDefault = aIn.readInt16(); + + std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] ); + for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ ) + pPositions[i] = aIn.readInt32(); + + // Import locales + LocaleItem* pUseAsDefaultItem = nullptr; + for( sal_Int32 i = 0; i < nLocaleCount; i++ ) + { + sal_Int32 nPos = pPositions[i]; + aIn.seek( nPos ); + + Locale aLocale; + aLocale.Language = aIn.readString(); + aLocale.Country = aIn.readString(); + aLocale.Variant = aIn.readString(); + + sal_Int32 nAfterStringPos = aIn.getPosition(); + sal_Int32 nSize = pPositions[i+1] - nAfterStringPos; + Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize ); + if( xInput.is() ) + { + LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale) ); + if( iDefault == i ) + pUseAsDefaultItem = pLocaleItem; + m_aLocaleItemVector.emplace_back( pLocaleItem ); + implReadPropertiesFile( pLocaleItem, xInput ); + } + } + + if( pUseAsDefaultItem != nullptr ) + setDefaultLocale( pUseAsDefaultItem->m_locale ); +} + + +// Private helper methods + +static bool checkNamingSceme( const OUString& aName, const OUString& aNameBase, + Locale& aLocale ) +{ + bool bSuccess = false; + + sal_Int32 nNameLen = aName.getLength(); + sal_Int32 nNameBaseLen = aNameBase.getLength(); + + // Name has to start with NameBase followed + // by a '_' and at least one more character + if( aName.startsWith( aNameBase ) && nNameBaseLen < nNameLen-1 && + aName[nNameBaseLen] == '_' ) + { + bSuccess = true; + + /* FIXME-BCP47: this uses '_' underscore character as separator and + * also appends Variant, which can't be blindly changed as it would + * violate the naming scheme in use. */ + + sal_Int32 iStart = nNameBaseLen + 1; + sal_Int32 iNext_ = aName.indexOf( '_', iStart ); + if( iNext_ != -1 && iNext_ < nNameLen-1 ) + { + aLocale.Language = aName.copy( iStart, iNext_ - iStart ); + + iStart = iNext_ + 1; + iNext_ = aName.indexOf( '_', iStart ); + if( iNext_ != -1 && iNext_ < nNameLen-1 ) + { + aLocale.Country = aName.copy( iStart, iNext_ - iStart ); + aLocale.Variant = aName.copy( iNext_ + 1 ); + } + else + aLocale.Country = aName.copy( iStart ); + } + else + aLocale.Language = aName.copy( iStart ); + } + return bSuccess; +} + +void StringResourcePersistenceImpl::implLoadAllLocales() +{ + for( auto& pLocaleItem : m_aLocaleItemVector ) + if( pLocaleItem ) + loadLocale( pLocaleItem.get() ); +} + +// Scan locale properties files helper +void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq ) +{ + Locale aDefaultLocale; + bool bDefaultFound = false; + + for( const OUString& aCompleteName : aContentSeq ) + { + OUString aPureName; + OUString aExtension; + sal_Int32 iDot = aCompleteName.lastIndexOf( '.' ); + sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' ); + if( iDot != -1 && iDot > iSlash) + { + sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0; + aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom ); + aExtension = aCompleteName.copy( iDot + 1 ); + } + + if ( aExtension == "properties" ) + { + //OUString aName = aInetObj.getBase(); + Locale aLocale; + + if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) ) + { + LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale), false ); + m_aLocaleItemVector.emplace_back( pLocaleItem ); + + if( m_pCurrentLocaleItem == nullptr ) + m_pCurrentLocaleItem = pLocaleItem; + + if( m_pDefaultLocaleItem == nullptr ) + { + m_pDefaultLocaleItem = pLocaleItem; + m_bDefaultModified = true; + } + } + } + else if( !bDefaultFound && aExtension == "default" ) + { + if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) ) + bDefaultFound = true; + } + } + if( bDefaultFound ) + { + LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false ); + if( pLocaleItem ) + { + m_pDefaultLocaleItem = pLocaleItem; + m_bDefaultModified = false; + } + } +} + +// Scan locale properties files +void StringResourcePersistenceImpl::implScanLocales() +{ + // Dummy implementation, method not called for this + // base class, but pure virtual not possible- +} + +bool StringResourcePersistenceImpl::loadLocale( LocaleItem* pLocaleItem ) +{ + bool bSuccess = false; + + OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" ); + if( pLocaleItem ) + { + if( pLocaleItem->m_bLoaded ) + { + bSuccess = true; + } + else + { + bSuccess = implLoadLocale( pLocaleItem ); + pLocaleItem->m_bLoaded = true; // = bSuccess??? -> leads to more tries + } + } + return bSuccess; +} + +bool StringResourcePersistenceImpl::implLoadLocale( LocaleItem* ) +{ + // Dummy implementation, method not called for this + // base class, but pure virtual not possible- + return false; +} + +static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem ) +{ + /* FIXME-BCP47: this uses '_' underscore character as separator and + * also appends Variant, which can't be blindly changed as it would + * violate the naming scheme in use. */ + + static const char aUnder[] = "_"; + + OSL_ENSURE( pLocaleItem, + "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL" ); + Locale aLocale = pLocaleItem->m_locale; + + OUString aRetStr = aUnder + aLocale.Language; + + OUString aCountry = aLocale.Country; + if( !aCountry.isEmpty() ) + { + aRetStr += aUnder + aCountry; + } + + OUString aVariant = aLocale.Variant; + if( !aVariant.isEmpty() ) + { + aRetStr += aUnder + aVariant; + } + return aRetStr; +} + +OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem + ( LocaleItem const * pLocaleItem, const OUString& aNameBase ) +{ + OUString aFileName = aNameBase; + if( aFileName.isEmpty() ) + aFileName = aNameBaseDefaultStr; + + aFileName += implGetNameScemeForLocaleItem( pLocaleItem ); + return aFileName; +} + +OUString StringResourcePersistenceImpl::implGetPathForLocaleItem + ( LocaleItem const * pLocaleItem, const OUString& aNameBase, + std::u16string_view aLocation, bool bDefaultFile ) +{ + OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase ); + INetURLObject aInetObj( aLocation ); + aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + if( bDefaultFile ) + aInetObj.setExtension( u"default" ); + else + aInetObj.setExtension( u"properties" ); + OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + return aCompleteFileName; +} + +// White space according to Java property files specification in +// http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream) +static bool isWhiteSpace( sal_Unicode c ) +{ + bool bWhite = ( c == 0x0020 || // space + c == 0x0009 || // tab + c == 0x000a || // line feed, not always handled by TextInputStream + c == 0x000d || // carriage return, not always handled by TextInputStream + c == 0x000C ); // form feed + return bWhite; +} + +static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri ) +{ + while( ri < nLen ) + { + if( !isWhiteSpace( pBuf[ri] ) ) + break; + ri++; + } +} + +static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal ) +{ + bool bRet = true; + if( c >= '0' && c <= '9' ) + nDigitVal = c - '0'; + else if( c >= 'a' && c <= 'f' ) + nDigitVal = c - 'a' + 10; + else if( c >= 'A' && c <= 'F' ) + nDigitVal = c - 'A' + 10; + else + bRet = false; + return bRet; +} + +static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri ) +{ + sal_Int32 i = ri; + + sal_Unicode cRet = 0; + sal_Unicode c = pBuf[i]; + switch( c ) + { + case 't': + cRet = 0x0009; + break; + case 'n': + cRet = 0x000a; + break; + case 'f': + cRet = 0x000c; + break; + case 'r': + cRet = 0x000d; + break; + case '\\': + cRet = '\\'; + break; + case 'u': + { + // Skip multiple u + i++; + while( i < nLen && pBuf[i] == 'u' ) + i++; + + // Process hex digits + sal_Int32 nDigitCount = 0; + sal_uInt16 nDigitVal; + while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) ) + { + cRet = 16 * cRet + nDigitVal; + + nDigitCount++; + if( nDigitCount == 4 ) + { + // Write back position + ri = i; + break; + } + i++; + } + break; + } + default: + cRet = c; + } + + return cRet; +} + +static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream, + OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf, + sal_Int32& nLen, sal_Int32& i ) +{ + if( !(i == nLen && bEscapePending) ) + return; + + bEscapePending = false; + + if( !xTextInputStream->isEOF() ) + { + aLine = xTextInputStream->readLine(); + nLen = aLine.getLength(); + pBuf = aLine.getStr(); + i = 0; + + skipWhites( pBuf, nLen, i ); + } +} + +bool StringResourcePersistenceImpl::implReadPropertiesFile + ( LocaleItem* pLocaleItem, const Reference< io::XInputStream >& xInputStream ) +{ + if( !xInputStream.is() || pLocaleItem == nullptr ) + return false; + + Reference< io::XTextInputStream2 > xTextInputStream = io::TextInputStream::create( m_xContext ); + + xTextInputStream->setInputStream( xInputStream ); + + OUString aEncodingStr = OUString::createFromAscii + ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) ); + xTextInputStream->setEncoding( aEncodingStr ); + + OUString aLine; + while( !xTextInputStream->isEOF() ) + { + aLine = xTextInputStream->readLine(); + + sal_Int32 nLen = aLine.getLength(); + if( 0 == nLen ) + continue; + const sal_Unicode* pBuf = aLine.getStr(); + OUStringBuffer aBuf; + sal_Unicode c = 0; + sal_Int32 i = 0; + + skipWhites( pBuf, nLen, i ); + if( i == nLen ) + continue; // line contains only white spaces + + // Comment? + c = pBuf[i]; + if( c == '#' || c == '!' ) + continue; + + // Scan key + OUString aResourceID; + bool bEscapePending = false; + bool bStrComplete = false; + while( i < nLen && !bStrComplete ) + { + c = pBuf[i]; + if( bEscapePending ) + { + aBuf.append( getEscapeChar( pBuf, nLen, i ) ); + bEscapePending = false; + } + else + { + if( c == '\\' ) + { + bEscapePending = true; + } + else + { + if( c == ':' || c == '=' || isWhiteSpace( c ) ) + bStrComplete = true; + else + aBuf.append( c ); + } + } + i++; + + CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i ); + if( i == nLen ) + bStrComplete = true; + + if( bStrComplete ) + aResourceID = aBuf.makeStringAndClear(); + } + + // Ignore lines with empty keys + if( aResourceID.isEmpty() ) + continue; + + // Scan value + skipWhites( pBuf, nLen, i ); + + OUString aValueStr; + bEscapePending = false; + bStrComplete = false; + while( i < nLen && !bStrComplete ) + { + c = pBuf[i]; + if( c == 0x000a || c == 0x000d ) // line feed/carriage return, not always handled by TextInputStream + { + i++; + } + else + { + if( bEscapePending ) + { + aBuf.append( getEscapeChar( pBuf, nLen, i ) ); + bEscapePending = false; + } + else if( c == '\\' ) + bEscapePending = true; + else + aBuf.append( c ); + i++; + + CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i ); + } + if( i == nLen ) + bStrComplete = true; + + if( bStrComplete ) + aValueStr = aBuf.makeStringAndClear(); + } + + // Push into table + pLocaleItem->m_aIdToStringMap[ aResourceID ] = aValueStr; + implScanIdForNumber( aResourceID ); + IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap; + rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++; + } + + return true; +} + + +static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal ) +{ + sal_Unicode cRet = ( nDigitVal < 10 ) ? ('0' + nDigitVal) : ('a' + (nDigitVal-10)); + return cRet; +} + +static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey ) +{ + if( cu == '\\' ) + { + aBuf.append( '\\' ); + aBuf.append( '\\' ); + } + else if( cu == 0x000a ) + { + aBuf.append( '\\' ); + aBuf.append( 'n' ); + } + else if( cu == 0x000d ) + { + aBuf.append( '\\' ); + aBuf.append( 'r' ); + } + else if( bKey && cu == '=' ) + { + aBuf.append( '\\' ); + aBuf.append( '=' ); + } + else if( bKey && cu == ':' ) + { + aBuf.append( '\\' ); + aBuf.append( ':' ); + } + // ISO/IEC 8859-1 range according to: + // http://en.wikipedia.org/wiki/ISO/IEC_8859-1 + else if( cu >= 0x20 && cu <= 0x7e ) + //TODO: Check why (cu >= 0xa0 && cu <= 0xFF) + //is encoded in sample properties files + //else if( (cu >= 0x20 && cu <= 0x7e) || + // (cu >= 0xa0 && cu <= 0xFF) ) + { + aBuf.append( cu ); + } + else + { + // Unicode encoding + aBuf.append( '\\' ); + aBuf.append( 'u' ); + + sal_uInt16 nVal = cu; + for( sal_uInt16 i = 0 ; i < 4 ; i++ ) + { + sal_uInt16 nDigit = nVal / 0x1000; + nVal -= nDigit * 0x1000; + nVal *= 0x10; + aBuf.append( getHexCharForDigit( nDigit ) ); + } + } +} + +static void implWriteStringWithEncoding( const OUString& aStr, + Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey ) +{ + static const sal_Unicode cLineFeed = 0xa; + + OUStringBuffer aBuf; + sal_Int32 nLen = aStr.getLength(); + const sal_Unicode* pSrc = aStr.getStr(); + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + sal_Unicode cu = pSrc[i]; + implWriteCharToBuffer( aBuf, cu, bKey ); + // TODO?: split long lines + } + if( !bKey ) + aBuf.append( cLineFeed ); + + OUString aWriteStr = aBuf.makeStringAndClear(); + xTextOutputStream->writeString( aWriteStr ); +} + +bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem, + const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment ) +{ + if( !xOutputStream.is() || pLocaleItem == nullptr ) + return false; + + bool bSuccess = false; + Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext); + + xTextOutputStream->setOutputStream( xOutputStream ); + + OUString aEncodingStr = OUString::createFromAscii + ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) ); + xTextOutputStream->setEncoding( aEncodingStr ); + + xTextOutputStream->writeString( aComment ); + xTextOutputStream->writeString( "\n" ); + + const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap; + if( !rHashMap.empty() ) + { + // Sort ids according to read order + const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap; + + // Find max/min index + auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(), + [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; }); + sal_Int32 nMinIndex = itMinMax.first->second; + sal_Int32 nMaxIndex = itMinMax.second->second; + sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1; + + // Create sorted array of pointers to the id strings + std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] ); + for(sal_Int32 i = 0 ; i < nTabSize ; i++ ) + pIdPtrs[i] = nullptr; + for( const auto& rIndex : rIndexMap ) + { + sal_Int32 nIndex = rIndex.second; + pIdPtrs[nIndex - nMinIndex] = &(rIndex.first); + } + + // Write lines in correct order + for(sal_Int32 i = 0 ; i < nTabSize ; i++ ) + { + const OUString* pStr = pIdPtrs[i]; + if( pStr != nullptr ) + { + OUString aResourceID = *pStr; + IdToStringMap::const_iterator it = rHashMap.find( aResourceID ); + if( it != rHashMap.end() ) + { + implWriteStringWithEncoding( aResourceID, xTextOutputStream, true ); + xTextOutputStream->writeString( "=" ); + OUString aValStr = (*it).second; + implWriteStringWithEncoding( aValStr, xTextOutputStream, false ); + } + } + } + } + + bSuccess = true; + + return bSuccess; +} + + +// StringResourceWithStorageImpl + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_StringResourceWithStorageImpl_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new StringResourceWithStorageImpl(context)); +} + + +StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext ) + : StringResourceWithStorageImpl_BASE( rxContext ) + , m_bStorageChanged( false ) +{ +} + + +StringResourceWithStorageImpl::~StringResourceWithStorageImpl() +{ +} + + +// XServiceInfo + + +OUString StringResourceWithStorageImpl::getImplementationName( ) +{ + return "com.sun.star.comp.scripting.StringResourceWithStorage"; +} + +sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames( ) +{ + return { "com.sun.star.resource.StringResourceWithStorage" }; +} + + +// XInitialization + + +void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments ) +{ + std::unique_lock aGuard( m_aMutex ); + + if ( aArguments.getLength() != 5 ) + { + throw RuntimeException( + "StringResourceWithStorageImpl::initialize: invalid number of arguments!" ); + } + + bool bOk = (aArguments[0] >>= m_xStorage); + if( bOk && !m_xStorage.is() ) + bOk = false; + + if( !bOk ) + { + throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid storage", Reference< XInterface >(), 0 ); + } + + implInitializeCommonParameters( aGuard, aArguments ); +} + + +// Forwarding calls to base class + +// XModifyBroadcaster +void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener ) +{ + StringResourceImpl::addModifyListener( aListener ); +} +void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener ) +{ + StringResourceImpl::removeModifyListener( aListener ); +} + +// XStringResourceResolver +OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID ) +{ + return StringResourceImpl::resolveString( ResourceID ) ; +} +OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale ) +{ + return StringResourceImpl::resolveStringForLocale( ResourceID, locale ); +} +sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID ) +{ + return StringResourceImpl::hasEntryForId( ResourceID ) ; +} +sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID, + const Locale& locale ) +{ + return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale ); +} +Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs( ) +{ + return StringResourceImpl::getResourceIDs(); +} +Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale + ( const Locale& locale ) +{ + return StringResourceImpl::getResourceIDsForLocale( locale ); +} +Locale StringResourceWithStorageImpl::getCurrentLocale() +{ + return StringResourceImpl::getCurrentLocale(); +} +Locale StringResourceWithStorageImpl::getDefaultLocale( ) +{ + return StringResourceImpl::getDefaultLocale(); +} +Sequence< Locale > StringResourceWithStorageImpl::getLocales( ) +{ + return StringResourceImpl::getLocales(); +} + +// XStringResourceManager +sal_Bool StringResourceWithStorageImpl::isReadOnly() +{ + return StringResourceImpl::isReadOnly(); +} +void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch ) +{ + StringResourceImpl::setCurrentLocale( locale, FindClosestMatch ); +} +void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale ) +{ + StringResourceImpl::setDefaultLocale( locale ); +} +void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str ) +{ + StringResourceImpl::setString( ResourceID, Str ); +} +void StringResourceWithStorageImpl::setStringForLocale + ( const OUString& ResourceID, const OUString& Str, const Locale& locale ) +{ + StringResourceImpl::setStringForLocale( ResourceID, Str, locale ); +} +void StringResourceWithStorageImpl::removeId( const OUString& ResourceID ) +{ + StringResourceImpl::removeId( ResourceID ); +} +void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale ) +{ + StringResourceImpl::removeIdForLocale( ResourceID, locale ); +} +void StringResourceWithStorageImpl::newLocale( const Locale& locale ) +{ + StringResourceImpl::newLocale( locale ); +} +void StringResourceWithStorageImpl::removeLocale( const Locale& locale ) +{ + StringResourceImpl::removeLocale( locale ); +} +sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId( ) +{ + return StringResourceImpl::getUniqueNumericId(); +} + +// XStringResourcePersistence +void StringResourceWithStorageImpl::store() +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" ); + + bool bStoreAll = m_bStorageChanged; + m_bStorageChanged = false; + if( !m_bModified && !bStoreAll ) + return; + + implStoreAtStorage( m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll ); + m_bModified = false; +} + +sal_Bool StringResourceWithStorageImpl::isModified( ) +{ + return StringResourcePersistenceImpl::isModified(); +} +void StringResourceWithStorageImpl::setComment( const OUString& Comment ) +{ + StringResourcePersistenceImpl::setComment( Comment ); +} +void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage, + const OUString& NameBase, const OUString& Comment ) +{ + StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment ); +} +void StringResourceWithStorageImpl::storeToURL( const OUString& URL, + const OUString& NameBase, const OUString& Comment, + const Reference< css::task::XInteractionHandler >& Handler ) +{ + StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler ); +} +Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary( ) +{ + return StringResourcePersistenceImpl::exportBinary(); +} +void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data ) +{ + StringResourcePersistenceImpl::importBinary( Data ); +} + + +// XStringResourceWithStorage + +void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage ) +{ + setStorage( Storage ); + store(); +} + +void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( !Storage.is() ) + { + throw IllegalArgumentException( "StringResourceWithStorageImpl::setStorage: invalid storage", Reference< XInterface >(), 0 ); + } + + implLoadAllLocales(); + + m_xStorage = Storage; + m_bStorageChanged = true; +} + + +// Private helper methods + + +// Scan locale properties files +void StringResourceWithStorageImpl::implScanLocales() +{ + if( m_xStorage.is() ) + { + Sequence< OUString > aContentSeq = m_xStorage->getElementNames(); + implScanLocaleNames( aContentSeq ); + } + + implLoadAllLocales(); +} + +// Loading +bool StringResourceWithStorageImpl::implLoadLocale( LocaleItem* pLocaleItem ) +{ + bool bSuccess = false; + try + { + OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties"; + + Reference< io::XStream > xElementStream = + m_xStorage->openStreamElement( aStreamName, ElementModes::READ ); + + if( xElementStream.is() ) + { + Reference< io::XInputStream > xInputStream = xElementStream->getInputStream(); + if( xInputStream.is() ) + { + bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream ); + xInputStream->closeInput(); + } + } + } + catch( uno::Exception& ) + {} + + return bSuccess; +} + + +// StringResourceWithLocationImpl + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_StringResourceWithLocationImpl_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new StringResourceWithLocationImpl(context)); +} + + + +StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext ) + : StringResourceWithLocationImpl_BASE( rxContext ) + , m_bLocationChanged( false ) +{ +} + + +StringResourceWithLocationImpl::~StringResourceWithLocationImpl() +{ +} + + +// XServiceInfo + + +OUString StringResourceWithLocationImpl::getImplementationName( ) +{ + return "com.sun.star.comp.scripting.StringResourceWithLocation"; +} + +sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames( ) +{ + return { "com.sun.star.resource.StringResourceWithLocation" }; +} + + +// XInitialization + + +void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments ) +{ + std::unique_lock aGuard( m_aMutex ); + + if ( aArguments.getLength() != 6 ) + { + throw RuntimeException( + "XInitialization::initialize: invalid number of arguments!" ); + } + + bool bOk = (aArguments[0] >>= m_aLocation); + sal_Int32 nLen = m_aLocation.getLength(); + if( bOk && nLen == 0 ) + { + bOk = false; + } + else + { + if( m_aLocation[nLen - 1] != '/' ) + m_aLocation += "/"; + } + + if( !bOk ) + { + throw IllegalArgumentException( "XInitialization::initialize: invalid URL", Reference< XInterface >(), 0 ); + } + + + bOk = (aArguments[5] >>= m_xInteractionHandler); + if( !bOk ) + { + throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid type", Reference< XInterface >(), 5 ); + } + + implInitializeCommonParameters( aGuard, aArguments ); +} + + +// Forwarding calls to base class + +// XModifyBroadcaster +void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener ) +{ + StringResourceImpl::addModifyListener( aListener ); +} +void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener ) +{ + StringResourceImpl::removeModifyListener( aListener ); +} + +// XStringResourceResolver +OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID ) +{ + return StringResourceImpl::resolveString( ResourceID ) ; +} +OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale ) +{ + return StringResourceImpl::resolveStringForLocale( ResourceID, locale ); +} +sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID ) +{ + return StringResourceImpl::hasEntryForId( ResourceID ) ; +} +sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID, + const Locale& locale ) +{ + return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale ); +} +Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs( ) +{ + return StringResourceImpl::getResourceIDs(); +} +Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale + ( const Locale& locale ) +{ + return StringResourceImpl::getResourceIDsForLocale( locale ); +} +Locale StringResourceWithLocationImpl::getCurrentLocale() +{ + return StringResourceImpl::getCurrentLocale(); +} +Locale StringResourceWithLocationImpl::getDefaultLocale( ) +{ + return StringResourceImpl::getDefaultLocale(); +} +Sequence< Locale > StringResourceWithLocationImpl::getLocales( ) +{ + return StringResourceImpl::getLocales(); +} + +// XStringResourceManager +sal_Bool StringResourceWithLocationImpl::isReadOnly() +{ + return StringResourceImpl::isReadOnly(); +} +void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch ) +{ + StringResourceImpl::setCurrentLocale( locale, FindClosestMatch ); +} +void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale ) +{ + StringResourceImpl::setDefaultLocale( locale ); +} +void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str ) +{ + StringResourceImpl::setString( ResourceID, Str ); +} +void StringResourceWithLocationImpl::setStringForLocale + ( const OUString& ResourceID, const OUString& Str, const Locale& locale ) +{ + StringResourceImpl::setStringForLocale( ResourceID, Str, locale ); +} +void StringResourceWithLocationImpl::removeId( const OUString& ResourceID ) +{ + StringResourceImpl::removeId( ResourceID ); +} +void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale ) +{ + StringResourceImpl::removeIdForLocale( ResourceID, locale ); +} +void StringResourceWithLocationImpl::newLocale( const Locale& locale ) +{ + StringResourceImpl::newLocale( locale ); +} +void StringResourceWithLocationImpl::removeLocale( const Locale& locale ) +{ + StringResourceImpl::removeLocale( locale ); +} +sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId( ) +{ + return StringResourceImpl::getUniqueNumericId(); +} + +// XStringResourcePersistence +void StringResourceWithLocationImpl::store() +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" ); + + bool bStoreAll = m_bLocationChanged; + m_bLocationChanged = false; + if( !m_bModified && !bStoreAll ) + return; + + Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl(); + implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment, + xFileAccess, true/*bUsedForStore*/, bStoreAll ); + m_bModified = false; +} + +sal_Bool StringResourceWithLocationImpl::isModified( ) +{ + return StringResourcePersistenceImpl::isModified(); +} +void StringResourceWithLocationImpl::setComment( const OUString& Comment ) +{ + StringResourcePersistenceImpl::setComment( Comment ); +} +void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage, + const OUString& NameBase, const OUString& Comment ) +{ + StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment ); +} +void StringResourceWithLocationImpl::storeToURL( const OUString& URL, + const OUString& NameBase, const OUString& Comment, + const Reference< css::task::XInteractionHandler >& Handler ) +{ + StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler ); +} +Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary( ) +{ + return StringResourcePersistenceImpl::exportBinary(); +} +void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data ) +{ + StringResourcePersistenceImpl::importBinary( Data ); +} + + +// XStringResourceWithLocation + +// XStringResourceWithLocation +void StringResourceWithLocationImpl::storeAsURL( const OUString& URL ) +{ + setURL( URL ); + store(); +} + +void StringResourceWithLocationImpl::setURL( const OUString& URL ) +{ + std::unique_lock aGuard( m_aMutex ); + implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" ); + + sal_Int32 nLen = URL.getLength(); + if( nLen == 0 ) + { + throw IllegalArgumentException( "StringResourceWithLocationImpl::setURL: invalid URL", Reference< XInterface >(), 0 ); + } + + implLoadAllLocales(); + + // Delete files at old location + implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment, + getFileAccessImpl(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ ); + + m_aLocation = URL; + m_bLocationChanged = true; +} + + +// Private helper methods + + +// Scan locale properties files +void StringResourceWithLocationImpl::implScanLocales() +{ + const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl(); + if( xFileAccess.is() && xFileAccess->isFolder( m_aLocation ) ) + { + Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false ); + implScanLocaleNames( aContentSeq ); + } +} + +// Loading +bool StringResourceWithLocationImpl::implLoadLocale( LocaleItem* pLocaleItem ) +{ + bool bSuccess = false; + + const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl(); + if( xFileAccess.is() ) + { + OUString aCompleteFileName = + implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation ); + + Reference< io::XInputStream > xInputStream; + try + { + xInputStream = xFileAccess->openFileRead( aCompleteFileName ); + } + catch( Exception& ) + {} + if( xInputStream.is() ) + { + bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream ); + xInputStream->closeInput(); + } + } + + return bSuccess; +} + +const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccessImpl() +{ + if( !m_xSFI.is() ) + { + m_xSFI = ucb::SimpleFileAccess::create(m_xContext); + + if( m_xSFI.is() && m_xInteractionHandler.is() ) + m_xSFI->setInteractionHandler( m_xInteractionHandler ); + } + return m_xSFI; +} + +} // namespace stringresource + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/stringresource/stringresource.hxx b/scripting/source/stringresource/stringresource.hxx new file mode 100644 index 000000000..7722da62f --- /dev/null +++ b/scripting/source/stringresource/stringresource.hxx @@ -0,0 +1,485 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/resource/XStringResourceWithStorage.hpp> +#include <com/sun/star/resource/XStringResourceWithLocation.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/ucb/XSimpleFileAccess3.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/interfacecontainer4.hxx> +#include <mutex> +#include <unordered_map> +#include <vector> + + +namespace stringresource +{ + + +// class stringresourceImpl + + +// Hashtable to map string ids to string +typedef std::unordered_map +< + OUString, + OUString +> +IdToStringMap; + +typedef std::unordered_map +< + OUString, + sal_Int32 +> +IdToIndexMap; + + +struct LocaleItem +{ + css::lang::Locale m_locale; + IdToStringMap m_aIdToStringMap; + IdToIndexMap m_aIdToIndexMap; + sal_Int32 m_nNextIndex; + bool m_bLoaded; + bool m_bModified; + + LocaleItem( css::lang::Locale locale, bool bLoaded=true ) + : m_locale( locale ) + , m_nNextIndex( 0 ) + , m_bLoaded( bLoaded ) + , m_bModified( false ) + {} +}; + +typedef ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::resource::XStringResourceManager > StringResourceImpl_BASE; + +class StringResourceImpl : public StringResourceImpl_BASE +{ +protected: + std::mutex m_aMutex; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + LocaleItem* m_pCurrentLocaleItem; + LocaleItem* m_pDefaultLocaleItem; + bool m_bDefaultModified; + + ::comphelper::OInterfaceContainerHelper4<css::util::XModifyListener> m_aListenerContainer; + + std::vector< std::unique_ptr<LocaleItem> > m_aLocaleItemVector; + std::vector< std::unique_ptr<LocaleItem> > m_aDeletedLocaleItemVector; + std::vector< std::unique_ptr<LocaleItem> > m_aChangedDefaultLocaleVector; + + bool m_bModified; + bool m_bReadOnly; + + sal_Int32 m_nNextUniqueNumericId; + + // Scans ResourceID to start with number and adapt m_nNextUniqueNumericId + void implScanIdForNumber( const OUString& ResourceID ); + const static sal_Int32 UNIQUE_NUMBER_NEEDS_INITIALISATION = -1; + + // Checks read only status and throws exception if it's true + /// @throws css::lang::NoSupportException + void implCheckReadOnly( const char* pExceptionMsg ); + + // Returns the LocalItem for a given locale, if it exists, otherwise NULL + // This method compares the locales exactly, no closest match search is performed + /// @throws css::lang::IllegalArgumentException + LocaleItem* getItemForLocale( const css::lang::Locale& locale, bool bException ); + + // Returns the LocalItem for a given locale, if it exists, otherwise NULL + // This method performs a closest match search, at least the language must match + LocaleItem* getClosestMatchItemForLocale( const css::lang::Locale& locale ); + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + void implSetCurrentLocale( std::unique_lock<std::mutex>& rGuard, const css::lang::Locale& locale, + bool FindClosestMatch, bool bUseDefaultIfNoMatch ); + + void implModified(std::unique_lock<std::mutex>&); + void implNotifyListeners(std::unique_lock<std::mutex>&); + + //=== Impl methods for ...ForLocale methods === + /// @throws css::resource::MissingResourceException + OUString implResolveString( const OUString& ResourceID, LocaleItem* pLocaleItem ); + bool implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem ); + css::uno::Sequence< OUString > implGetResourceIDs( LocaleItem* pLocaleItem ); + void implSetString( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, + const OUString& Str, LocaleItem* pLocaleItem ); + /// @throws css::resource::MissingResourceException + void implRemoveId( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem ); + + // Method to load a locale if necessary, returns true if loading was + // successful. Default implementation in base class just returns true. + virtual bool loadLocale( LocaleItem* pLocaleItem ); + + virtual void implLoadAllLocales(); + +public: + explicit StringResourceImpl( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~StringResourceImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XStringResourceResolver + virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override; + virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override; + virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale + ( const css::lang::Locale& locale ) override; + virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override; + virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override; + virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override; + + // XStringResourceManager + virtual sal_Bool SAL_CALL isReadOnly() override; + virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override; + virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override; + virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeId( const OUString& ResourceID ) override; + virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override; + virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override; + }; + +typedef ::cppu::ImplInheritanceHelper< + StringResourceImpl, + css::resource::XStringResourcePersistence > StringResourcePersistenceImpl_BASE; + +class BinaryOutput; + +class StringResourcePersistenceImpl : public StringResourcePersistenceImpl_BASE +{ +protected: + OUString m_aNameBase; + OUString m_aComment; + + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void implInitializeCommonParameters( std::unique_lock<std::mutex>& rGuard, const css::uno::Sequence< css::uno::Any >& aArguments ); + + // Scan locale properties files + virtual void implScanLocales(); + + // Method to load a locale if necessary, returns true if loading was successful + virtual bool loadLocale( LocaleItem* pLocaleItem ) override; + + // does the actual loading + virtual bool implLoadLocale( LocaleItem* pLocaleItem ); + + virtual void implLoadAllLocales() override; + + void implScanLocaleNames( const css::uno::Sequence< OUString >& aContentSeq ); + static OUString implGetFileNameForLocaleItem( LocaleItem const * pLocaleItem, const OUString& aNameBase ); + static OUString implGetPathForLocaleItem( LocaleItem const * pLocaleItem, const OUString& aNameBase, + std::u16string_view aLocation, bool bDefaultFile=false ); + + bool implReadPropertiesFile( LocaleItem* pLocaleItem, + const css::uno::Reference< css::io::XInputStream >& xInput ); + + bool implWritePropertiesFile( LocaleItem const * pLocaleItem, + const css::uno::Reference< css::io::XOutputStream >& xOutputStream, + const OUString& aComment ); + + void implWriteLocaleBinary( LocaleItem* pLocaleItem, BinaryOutput& rOut ); + + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void implStoreAtStorage + ( + const OUString& aNameBase, + const OUString& aComment, + const css::uno::Reference< css::embed::XStorage >& Storage, + bool bUsedForStore, + bool bStoreAll + ); + + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void implKillRemovedLocaleFiles + ( + std::u16string_view Location, + const OUString& aNameBase, + const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess + ); + + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void implKillChangedDefaultFiles + ( + std::u16string_view Location, + const OUString& aNameBase, + const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess + ); + + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void implStoreAtLocation + ( + std::u16string_view Location, + const OUString& aNameBase, + const OUString& aComment, + const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess, + bool bUsedForStore, + bool bStoreAll, + bool bKillAll = false + ); + +public: + explicit StringResourcePersistenceImpl( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~StringResourcePersistenceImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XStringResourceResolver + virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override; + virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override; + virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale + ( const css::lang::Locale& locale ) override; + virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override; + virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override; + virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override; + + // XStringResourceManager + virtual sal_Bool SAL_CALL isReadOnly() override; + virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override; + virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override; + virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeId( const OUString& ResourceID ) override; + virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override; + virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override; + + // XStringResourcePersistence + virtual void SAL_CALL store( ) override; + virtual sal_Bool SAL_CALL isModified( ) override; + virtual void SAL_CALL setComment( const OUString& Comment ) override; + virtual void SAL_CALL storeToStorage + ( const css::uno::Reference< css::embed::XStorage >& Storage, + const OUString& NameBase, const OUString& Comment ) override; + virtual void SAL_CALL storeToURL( const OUString& URL, const OUString& NameBase, + const OUString& Comment, const css::uno::Reference + < css::task::XInteractionHandler >& Handler ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL exportBinary( ) override; + virtual void SAL_CALL importBinary( const css::uno::Sequence< ::sal_Int8 >& Data ) override; +}; + + +typedef ::cppu::ImplInheritanceHelper< + StringResourcePersistenceImpl, + css::lang::XInitialization, + css::resource::XStringResourceWithStorage > StringResourceWithStorageImpl_BASE; + +class StringResourceWithStorageImpl : public StringResourceWithStorageImpl_BASE +{ + css::uno::Reference< css::embed::XStorage > m_xStorage; + bool m_bStorageChanged; + + virtual void implScanLocales() override; + virtual bool implLoadLocale( LocaleItem* pLocaleItem ) override; + +public: + explicit StringResourceWithStorageImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~StringResourceWithStorageImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XStringResourceResolver + virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override; + virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override; + virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale + ( const css::lang::Locale& locale ) override; + virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override; + virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override; + virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override; + + // XStringResourceManager + virtual sal_Bool SAL_CALL isReadOnly() override; + virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override; + virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override; + virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeId( const OUString& ResourceID ) override; + virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override; + virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override; + + // XStringResourcePersistence + virtual void SAL_CALL store( ) override; + virtual sal_Bool SAL_CALL isModified( ) override; + virtual void SAL_CALL setComment( const OUString& Comment ) override; + virtual void SAL_CALL storeToStorage + ( const css::uno::Reference< css::embed::XStorage >& Storage, + const OUString& NameBase, const OUString& Comment ) override; + virtual void SAL_CALL storeToURL( const OUString& URL, const OUString& NameBase, + const OUString& Comment, const css::uno::Reference + < css::task::XInteractionHandler >& Handler ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL exportBinary( ) override; + virtual void SAL_CALL importBinary( const css::uno::Sequence< ::sal_Int8 >& Data ) override; + + // XStringResourceWithStorage + virtual void SAL_CALL storeAsStorage + ( const css::uno::Reference< css::embed::XStorage >& Storage ) override; + virtual void SAL_CALL setStorage + ( const css::uno::Reference< css::embed::XStorage >& Storage ) override; +}; + + +typedef ::cppu::ImplInheritanceHelper< + StringResourcePersistenceImpl, + css::lang::XInitialization, + css::resource::XStringResourceWithLocation > StringResourceWithLocationImpl_BASE; + +class StringResourceWithLocationImpl : public StringResourceWithLocationImpl_BASE +{ + OUString m_aLocation; + bool m_bLocationChanged; + css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFI; + css::uno::Reference< css::task::XInteractionHandler > m_xInteractionHandler; + + const css::uno::Reference< css::ucb::XSimpleFileAccess3 > & getFileAccessImpl(); + + virtual void implScanLocales() override; + virtual bool implLoadLocale( LocaleItem* pLocaleItem ) override; + +public: + explicit StringResourceWithLocationImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~StringResourceWithLocationImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XStringResourceResolver + virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override; + virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override; + virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale + ( const css::lang::Locale& locale ) override; + virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override; + virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override; + virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override; + + // XStringResourceManager + virtual sal_Bool SAL_CALL isReadOnly() override; + virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override; + virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override; + virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeId( const OUString& ResourceID ) override; + virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID, + const css::lang::Locale& locale ) override; + virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override; + virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override; + virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override; + + // XStringResourcePersistence + virtual void SAL_CALL store( ) override; + virtual sal_Bool SAL_CALL isModified( ) override; + virtual void SAL_CALL setComment( const OUString& Comment ) override; + virtual void SAL_CALL storeToStorage + ( const css::uno::Reference< css::embed::XStorage >& Storage, + const OUString& NameBase, const OUString& Comment ) override; + virtual void SAL_CALL storeToURL( const OUString& URL, const OUString& NameBase, + const OUString& Comment, const css::uno::Reference + < css::task::XInteractionHandler >& Handler ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL exportBinary( ) override; + virtual void SAL_CALL importBinary( const css::uno::Sequence< ::sal_Int8 >& Data ) override; + + // XStringResourceWithLocation + virtual void SAL_CALL storeAsURL( const OUString& URL ) override; + virtual void SAL_CALL setURL( const OUString& URL ) override; +}; + + +} // namespace stringtable + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/vbaevents/eventhelper.cxx b/scripting/source/vbaevents/eventhelper.cxx new file mode 100644 index 000000000..9da09b734 --- /dev/null +++ b/scripting/source/vbaevents/eventhelper.cxx @@ -0,0 +1,982 @@ +/* -*- 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/macros.h> +#include <sal/log.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/uno3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <tools/diagnose_ex.h> + +#include <ooo/vba/XVBAToOOEventDescGen.hpp> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> + +#include <com/sun/star/frame/XModel.hpp> + +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> + +#include <com/sun/star/container/XNamed.hpp> + +#include <com/sun/star/drawing/XControlShape.hpp> + +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XDialog.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/awt/XFixedText.hpp> +#include <com/sun/star/awt/XTextComponent.hpp> +#include <com/sun/star/awt/XComboBox.hpp> +#include <com/sun/star/awt/XRadioButton.hpp> +#include <com/sun/star/awt/XListBox.hpp> + +#include <sfx2/objsh.hxx> +#include <basic/basmgr.hxx> +#include <filter/msfilter/msvbahelper.hxx> +#include <vbahelper/vbareturntypes.hxx> + +#include <com/sun/star/script/XScriptListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/evtmethodhelper.hxx> + +#include <vector> +#include <unordered_map> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::uno; +using namespace ::ooo::vba; + +// Some constants +constexpr std::u16string_view DELIM = u"::"; +constexpr sal_Int32 DELIMLEN = DELIM.size(); + +static bool isKeyEventOk( awt::KeyEvent& evt, const Sequence< Any >& params ) +{ + return params.hasElements() && ( params[ 0 ] >>= evt ); +} + +static bool isMouseEventOk( awt::MouseEvent& evt, const Sequence< Any >& params ) +{ + return params.hasElements() && ( params[ 0 ] >>= evt ); +} + +static Sequence< Any > ooMouseEvtToVBADblClick( const Sequence< Any >& params ) +{ + awt::MouseEvent evt; + + if ( !( isMouseEventOk(evt, params)) || + (evt.ClickCount != 2) ) + return Sequence< Any >(); + // give back orig params, this will signal that the event is good + return params; +} + +static Sequence< Any > ooMouseEvtToVBAMouseEvt( const Sequence< Any >& params ) +{ + awt::MouseEvent evt; + + if ( !isMouseEventOk(evt, params) ) + return Sequence< Any >(); + + Sequence< Any > translatedParams{ Any(evt.Buttons), // Buttons + Any(evt.Modifiers), // Shift + Any(evt.X), // X + Any(evt.Y) }; // Y + return translatedParams; +} + +static Sequence< Any > ooKeyPressedToVBAKeyPressed( const Sequence< Any >& params ) +{ + awt::KeyEvent evt; + + if ( !isKeyEventOk( evt, params ) ) + return Sequence< Any >(); + + Reference< msforms::XReturnInteger> xKeyCode = new ReturnInteger( sal_Int32( evt.KeyCode ) ); + Sequence< Any > translatedParams{ Any(xKeyCode) }; + return translatedParams; +} + +static Sequence< Any > ooKeyPressedToVBAKeyUpDown( const Sequence< Any >& params ) +{ + awt::KeyEvent evt; + + if ( !isKeyEventOk( evt, params ) ) + return Sequence< Any >(); + + Reference< msforms::XReturnInteger> xKeyCode = new ReturnInteger( evt.KeyCode ); + sal_Int8 shift = sal::static_int_cast<sal_Int8>( evt.Modifiers ); + + // #TODO check whether values from OOO conform to values generated from vba + Sequence< Any > translatedParams{ Any(xKeyCode), Any(shift) }; + return translatedParams; +} + +typedef Sequence< Any > (*Translator)(const Sequence< Any >&); + +namespace { + +//expand the "TranslateInfo" struct to support more kinds of events +struct TranslateInfo +{ + OUString sVBAName; //vba event name + Translator toVBA; //the method to convert OO event parameters to VBA event parameters + bool (*ApproveRule)(const ScriptEvent& evt, void const * pPara); //this method is used to determine which types of controls should execute the event + void const *pPara; //Parameters for the above approve method +}; + +} + +typedef std::unordered_map< + OUString, + std::vector< TranslateInfo > > EventInfoHash; + +namespace { + +struct TranslatePropMap +{ + OUString sEventInfo; //OO event name + TranslateInfo aTransInfo; +}; + +} + +static bool ApproveAll(const ScriptEvent& evt, void const * pPara); //allow all types of controls to execute the event +static bool ApproveType(const ScriptEvent& evt, void const * pPara); //certain types of controls should execute the event, those types are given by pPara +static bool DenyType(const ScriptEvent& evt, void const * pPara); //certain types of controls should not execute the event, those types are given by pPara +static bool DenyMouseDrag(const ScriptEvent& evt, void const * pPara); //used for VBA MouseMove event when "Shift" key is pressed + +namespace { + +struct TypeList +{ + uno::Type const * pTypeList; + int nListLength; +}; + +} + +Type const typeXFixedText = cppu::UnoType<awt::XFixedText>::get(); +Type const typeXTextComponent = cppu::UnoType<awt::XTextComponent>::get(); +Type const typeXComboBox = cppu::UnoType<awt::XComboBox>::get(); +Type const typeXRadioButton = cppu::UnoType<awt::XRadioButton>::get(); +Type const typeXListBox = cppu::UnoType<awt::XListBox>::get(); + + +TypeList const fixedTextList = {&typeXFixedText, 1}; +TypeList const textCompList = {&typeXTextComponent, 1}; +TypeList const radioButtonList = {&typeXRadioButton, 1}; +TypeList const comboBoxList = {&typeXComboBox, 1}; +TypeList const listBoxList = {&typeXListBox, 1}; + +//this array stores the OO event to VBA event translation info +static TranslatePropMap aTranslatePropMap_Impl[] = +{ + { OUString("actionPerformed"), { OUString("_Change"), nullptr, DenyType, static_cast<void const *>(&radioButtonList) } }, + // actionPerformed ooo event + { OUString("actionPerformed"), { OUString("_Click"), nullptr, ApproveAll, nullptr } }, + { OUString("itemStateChanged"), { OUString("_Change"), nullptr, ApproveType, static_cast<void const *>(&radioButtonList) } }, + // itemStateChanged ooo event + { OUString("itemStateChanged"), { OUString("_Click"), nullptr, ApproveType, static_cast<void const *>(&comboBoxList) } }, + + { OUString("itemStateChanged"), { OUString("_Click"), nullptr, ApproveType, static_cast<void const *>(&listBoxList) } }, + // changed ooo event + { OUString("changed"), { OUString("_Change"), nullptr, ApproveAll, nullptr } }, + + // focusGained ooo event + { OUString("focusGained"), { OUString("_GotFocus"), nullptr, ApproveAll, nullptr } }, + + // focusLost ooo event + { OUString("focusLost"), { OUString("_LostFocus"), nullptr, ApproveAll, nullptr } }, + { OUString("focusLost"), { OUString("_Exit"), nullptr, ApproveType, static_cast<void const *>(&textCompList) } }, // support VBA TextBox_Exit event + + // adjustmentValueChanged ooo event + { OUString("adjustmentValueChanged"), { OUString("_Scroll"), nullptr, ApproveAll, nullptr } }, + { OUString("adjustmentValueChanged"), { OUString("_Change"), nullptr, ApproveAll, nullptr } }, + + // textChanged ooo event + { OUString("textChanged"), { OUString("_Change"), nullptr, ApproveAll, nullptr } }, + + // keyReleased ooo event + { OUString("keyReleased"), { OUString("_KeyUp"), ooKeyPressedToVBAKeyUpDown, ApproveAll, nullptr } }, + + // mouseReleased ooo event + { OUString("mouseReleased"), { OUString("_Click"), ooMouseEvtToVBAMouseEvt, ApproveType, static_cast<void const *>(&fixedTextList) } }, + { OUString("mouseReleased"), { OUString("_MouseUp"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } }, + + // mousePressed ooo event + { OUString("mousePressed"), { OUString("_MouseDown"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } }, + { OUString("mousePressed"), { OUString("_DblClick"), ooMouseEvtToVBADblClick, ApproveAll, nullptr } }, + + // mouseMoved ooo event + { OUString("mouseMoved"), { OUString("_MouseMove"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } }, + { OUString("mouseDragged"), { OUString("_MouseMove"), ooMouseEvtToVBAMouseEvt, DenyMouseDrag, nullptr } }, + + // keyPressed ooo event + { OUString("keyPressed"), { OUString("_KeyDown"), ooKeyPressedToVBAKeyUpDown, ApproveAll, nullptr } }, + { OUString("keyPressed"), { OUString("_KeyPress"), ooKeyPressedToVBAKeyPressed, ApproveAll, nullptr } } +}; + +static EventInfoHash& getEventTransInfo() +{ + static EventInfoHash eventTransInfo = []() + { + EventInfoHash tmp; + OUString sEventInfo; + TranslatePropMap* pTransProp = aTranslatePropMap_Impl; + int nCount = SAL_N_ELEMENTS(aTranslatePropMap_Impl); + + int i = 0; + while (i < nCount) + { + sEventInfo = pTransProp->sEventInfo; + std::vector< TranslateInfo > infoList; + do + { + infoList.push_back( pTransProp->aTransInfo ); + pTransProp++; + i++; + }while(i < nCount && sEventInfo == pTransProp->sEventInfo); + tmp[sEventInfo] = std::move(infoList); + } + return tmp; + }(); + return eventTransInfo; +} + + +// Helper class + +namespace { + +class ScriptEventHelper +{ +public: + explicit ScriptEventHelper( const Reference< XInterface >& xControl ); + explicit ScriptEventHelper( const OUString& sCntrlServiceName ); + ~ScriptEventHelper(); + Sequence< ScriptEventDescriptor > createEvents( const OUString& sCodeName ); + Sequence< OUString > getEventListeners() const; +private: + Reference< XComponentContext > m_xCtx; + Reference< XInterface > m_xControl; + bool m_bDispose; +}; + +} + +static bool +eventMethodToDescriptor( std::u16string_view rEventMethod, ScriptEventDescriptor& evtDesc, const OUString& sCodeName ) +{ + // format of ControlListener is TypeName::methodname e.g. + // "com.sun.star.awt.XActionListener::actionPerformed" or + // "XActionListener::actionPerformed + + OUString sMethodName; + OUString sTypeName; + size_t nDelimPos = rEventMethod.find( DELIM ); + if ( nDelimPos == std::u16string_view::npos ) + { + return false; + } + sMethodName = rEventMethod.substr( nDelimPos + DELIMLEN ); + sTypeName = rEventMethod.substr( 0, nDelimPos ); + + EventInfoHash& infos = getEventTransInfo(); + + // Only create an ScriptEventDescriptor for an event we can translate + // or emulate + if ( !sMethodName.isEmpty() + && !sTypeName.isEmpty() + && ( infos.find( sMethodName ) != infos.end() ) ) + { + // just fill in CodeName, when the event fires the other + // info is gathered from the event source to determine what + // event handler we try to call + evtDesc.ScriptCode = sCodeName; + evtDesc.ListenerType = sTypeName; + evtDesc.EventMethod = sMethodName; + + // set this it VBAInterop, ensures that it doesn't + // get persisted or shown in property editors + evtDesc.ScriptType = "VBAInterop"; + return true; + } + return false; + +} + +ScriptEventHelper::ScriptEventHelper( const Reference< XInterface >& xControl ) : + m_xCtx( comphelper::getProcessComponentContext() ), + m_xControl( xControl ), + m_bDispose( false ) +{} + +ScriptEventHelper::ScriptEventHelper( const OUString& sCntrlServiceName ) : + m_xCtx( comphelper::getProcessComponentContext() ), + m_bDispose( true ) +{ + m_xControl.set( m_xCtx->getServiceManager()->createInstanceWithContext( sCntrlServiceName, m_xCtx ), uno::UNO_QUERY ); +} + +ScriptEventHelper::~ScriptEventHelper() +{ + // dispose control ( and remove any associated event registrations ) + if ( m_bDispose ) + { + try + { + uno::Reference< lang::XComponent > xComp( m_xControl, uno::UNO_QUERY_THROW ); + xComp->dispose(); + } + // destructor can't throw + catch( uno::Exception& ) + { + } + } +} + +Sequence< OUString > +ScriptEventHelper::getEventListeners() const +{ + std::vector< OUString > eventMethods; + + Reference< beans::XIntrospection > xIntrospection = beans::theIntrospection::get( m_xCtx ); + + Reference< beans::XIntrospectionAccess > xIntrospectionAccess = + xIntrospection->inspect( Any( m_xControl ) ); + const Sequence< Type > aControlListeners = + xIntrospectionAccess->getSupportedListeners(); + for ( const Type& listType : aControlListeners ) + { + OUString sFullTypeName = listType.getTypeName(); + const Sequence< OUString > sMeths = + comphelper::getEventMethodsForType( listType ); + std::transform(sMeths.begin(), sMeths.end(), std::back_inserter(eventMethods), + [&sFullTypeName](const OUString& rMeth) -> OUString { return sFullTypeName + DELIM + rMeth; }); + } + + return comphelper::containerToSequence(eventMethods); +} + +Sequence< ScriptEventDescriptor > +ScriptEventHelper::createEvents( const OUString& sCodeName ) +{ + const Sequence< OUString > aControlListeners = getEventListeners(); + sal_Int32 nLength = aControlListeners.getLength(); + + Sequence< ScriptEventDescriptor > aDest( nLength ); + sal_Int32 nEvts = 0; + for ( OUString const & i : aControlListeners) + { + // from getListeners eventName is of form + // "com.sun.star.awt.XActionListener::actionPerformed" + // we need to strip "com.sun.star.awt." from that for form + // controls + ScriptEventDescriptor evtDesc; + if ( eventMethodToDescriptor( i, evtDesc, sCodeName ) ) + { + sal_Int32 dIndex = nEvts; + ++nEvts; + if ( nEvts > aDest.getLength() ) + aDest.realloc( nEvts );// should never happen + aDest.getArray()[ dIndex ] = evtDesc; + } + } + aDest.realloc( nEvts ); + + return aDest; +} + + +typedef ::cppu::WeakImplHelper< container::XNameContainer > NameContainer_BASE; + +namespace { + +class ReadOnlyEventsNameContainer : public NameContainer_BASE +{ +public: + ReadOnlyEventsNameContainer( const Sequence< OUString >& eventMethods, const OUString& sCodeName ); + // XNameContainer + + virtual void SAL_CALL insertByName( const OUString&, const Any& ) override + { + throw RuntimeException("ReadOnly container" ); + + } + virtual void SAL_CALL removeByName( const OUString& ) override + { + throw RuntimeException("ReadOnly container" ); + } + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString&, const Any& ) override + { + throw RuntimeException("ReadOnly container" ); + + } + + // XNameAccess + virtual Any SAL_CALL getByName( const OUString& aName ) override; + virtual Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType( ) override + { return cppu::UnoType<OUString>::get(); } + virtual sal_Bool SAL_CALL hasElements( ) override + { return !m_hEvents.empty(); } +private: + +typedef std::unordered_map< OUString, Any > EventSupplierHash; + + EventSupplierHash m_hEvents; +}; + +} + +ReadOnlyEventsNameContainer::ReadOnlyEventsNameContainer( const Sequence< OUString >& eventMethods, const OUString& sCodeName ) +{ + for ( const OUString& rSrc : eventMethods ) + { + Any aDesc; + ScriptEventDescriptor evtDesc; + if ( eventMethodToDescriptor( rSrc, evtDesc, sCodeName ) ) + { + aDesc <<= evtDesc; + m_hEvents[ rSrc ] = aDesc; + } + } +} + +Any SAL_CALL +ReadOnlyEventsNameContainer::getByName( const OUString& aName ){ + EventSupplierHash::const_iterator it = m_hEvents.find( aName ); + if ( it == m_hEvents.end() ) + throw container::NoSuchElementException(); + return it->second; +} + +Sequence< OUString > SAL_CALL +ReadOnlyEventsNameContainer::getElementNames( ) +{ + return comphelper::mapKeysToSequence(m_hEvents); +} + +sal_Bool SAL_CALL +ReadOnlyEventsNameContainer::hasByName( const OUString& aName ) +{ + EventSupplierHash::const_iterator it = m_hEvents.find( aName ); + if ( it == m_hEvents.end() ) + return false; + return true; +} + +namespace { + +class ReadOnlyEventsSupplier : public ::cppu::WeakImplHelper< XScriptEventsSupplier > +{ +public: + ReadOnlyEventsSupplier( const Sequence< OUString >& eventMethods, const OUString& sCodeName ) + { m_xNameContainer = new ReadOnlyEventsNameContainer( eventMethods, sCodeName ); } + + // XScriptEventSupplier + virtual Reference< container::XNameContainer > SAL_CALL getEvents( ) override { return m_xNameContainer; } +private: + Reference< container::XNameContainer > m_xNameContainer; +}; + +} + +typedef ::cppu::WeakImplHelper< XScriptListener, util::XCloseListener, lang::XInitialization, css::lang::XServiceInfo > EventListener_BASE; + +#define EVENTLSTNR_PROPERTY_ID_MODEL 1 +constexpr OUStringLiteral EVENTLSTNR_PROPERTY_MODEL = u"Model"; + +namespace { + +class EventListener : public EventListener_BASE + ,public ::comphelper::OMutexAndBroadcastHelper + ,public ::comphelper::OPropertyContainer + ,public ::comphelper::OPropertyArrayUsageHelper< EventListener > +{ + +public: + EventListener(); + // XEventListener + virtual void SAL_CALL disposing(const lang::EventObject& Source) override; + using cppu::OPropertySetHelper::disposing; + + // XScriptListener + virtual void SAL_CALL firing(const ScriptEvent& evt) override; + virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override; + // XCloseListener + virtual void SAL_CALL queryClosing( const lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const lang::EventObject& Source ) override; + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + virtual void SAL_CALL setFastPropertyValue( sal_Int32 nHandle, const css::uno::Any& rValue ) override + { + if ( nHandle == EVENTLSTNR_PROPERTY_ID_MODEL ) + { + uno::Reference< frame::XModel > xModel( rValue, uno::UNO_QUERY ); + if( xModel != m_xModel) + { + // Remove the listener from the old XCloseBroadcaster. + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xModel, uno::UNO_QUERY ); + if (xCloseBroadcaster.is()) + { + xCloseBroadcaster->removeCloseListener( this ); + } + // Add the listener into the new XCloseBroadcaster. + xCloseBroadcaster.set( xModel, uno::UNO_QUERY ); + if (xCloseBroadcaster.is()) + { + xCloseBroadcaster->addCloseListener( this ); + } + } + } + OPropertyContainer::setFastPropertyValue( nHandle, rValue ); + if ( nHandle == EVENTLSTNR_PROPERTY_ID_MODEL ) + setShellFromModel(); + } + + OUString SAL_CALL getImplementationName() override + { + return "ooo.vba.EventListener"; + } + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { getImplementationName() }; + } + +protected: + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + +private: + void setShellFromModel(); + /// @throws RuntimeException + void firing_Impl( const ScriptEvent& evt, Any *pSyncRet ); + + Reference< frame::XModel > m_xModel; + bool m_bDocClosed; + SfxObjectShell* mpShell; +}; + +} + +EventListener::EventListener() : +OPropertyContainer(GetBroadcastHelper()), m_bDocClosed(false), mpShell( nullptr ) +{ + registerProperty( EVENTLSTNR_PROPERTY_MODEL, EVENTLSTNR_PROPERTY_ID_MODEL, + beans::PropertyAttribute::TRANSIENT, &m_xModel, cppu::UnoType<decltype(m_xModel)>::get() ); +} + +void +EventListener::setShellFromModel() +{ + // reset mpShell + mpShell = nullptr; + SfxObjectShell* pShell = SfxObjectShell::GetFirst(); + while ( m_xModel.is() && pShell ) + { + if ( pShell->GetModel() == m_xModel ) + { + mpShell = pShell; + break; + } + pShell = SfxObjectShell::GetNext( *pShell ); + } +} + +//XEventListener +void +EventListener::disposing(const lang::EventObject&) +{ +} + +//XScriptListener + +void SAL_CALL +EventListener::firing(const ScriptEvent& evt) +{ + firing_Impl( evt, nullptr ); +} + +Any SAL_CALL +EventListener::approveFiring(const ScriptEvent& evt) +{ + Any ret; + firing_Impl( evt, &ret ); + return ret; +} + +// XCloseListener +void SAL_CALL +EventListener::queryClosing( const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ ) +{ + //Nothing to do +} + +void SAL_CALL +EventListener::notifyClosing( const lang::EventObject& /*Source*/ ) +{ + m_bDocClosed = true; + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xModel, uno::UNO_QUERY ); + if (xCloseBroadcaster.is()) + { + xCloseBroadcaster->removeCloseListener( this ); + } +} + +// XInitialization +void SAL_CALL +EventListener::initialize( const Sequence< Any >& aArguments ) +{ + if ( aArguments.getLength() == 1 ) + aArguments[0] >>= m_xModel; + SAL_INFO( + "scripting", + "args " << aArguments.getLength() << " m_xModel " << m_xModel.is()); +} + +// XInterface + +IMPLEMENT_FORWARD_XINTERFACE2( EventListener, EventListener_BASE, OPropertyContainer ) + +// XTypeProvider + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( EventListener, EventListener_BASE, OPropertyContainer ) + +// OPropertySetHelper + +::cppu::IPropertyArrayHelper& +EventListener::getInfoHelper( ) +{ + return *getArrayHelper(); +} + +// OPropertyArrayUsageHelper + +::cppu::IPropertyArrayHelper* +EventListener::createArrayHelper( ) const +{ + Sequence< beans::Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +// XPropertySet +Reference< beans::XPropertySetInfo > +EventListener::getPropertySetInfo( ) +{ + Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +//decide if the control should execute the event +bool ApproveAll(SAL_UNUSED_PARAMETER const ScriptEvent&, SAL_UNUSED_PARAMETER void const * ) +{ + return true; +} + +//for the given control type in evt.Arguments[0], look for if it appears in the type list in pPara +static bool FindControl(const ScriptEvent& evt, void const * pPara) +{ + lang::EventObject aEvent; + evt.Arguments[ 0 ] >>= aEvent; + uno::Reference< uno::XInterface > xInterface( aEvent.Source, uno::UNO_QUERY ); + + TypeList const * pTypeListInfo = static_cast<TypeList const *>(pPara); + Type const * pType = pTypeListInfo->pTypeList; + int nLen = pTypeListInfo->nListLength; + + for (int i = 0; i < nLen; i++) + { + if ( xInterface->queryInterface( *pType ).hasValue() ) + { + return true; + } + pType++; + } + + return false; +} + +//if the given control type in evt.Arguments[0] appears in the type list in pPara, then approve the execution +bool ApproveType(const ScriptEvent& evt, void const * pPara) +{ + return FindControl(evt, pPara); +} + +//if the given control type in evt.Arguments[0] appears in the type list in pPara, then deny the execution +bool DenyType(const ScriptEvent& evt, void const * pPara) +{ + return !FindControl(evt, pPara); +} + +//when mouse is moving, either the mouse button is pressed or some key is pressed can trigger the OO mouseDragged event, +//the former should be denied, and the latter allowed, only by doing so can the VBA MouseMove event when the "Shift" key is +//pressed can be correctly triggered +bool DenyMouseDrag(const ScriptEvent& evt, SAL_UNUSED_PARAMETER void const * ) +{ + awt::MouseEvent aEvent; + evt.Arguments[ 0 ] >>= aEvent; + return aEvent.Buttons == 0; +} + + +// EventListener + +void +EventListener::firing_Impl(const ScriptEvent& evt, Any* pRet ) +{ + // let default handlers deal with non vba stuff + if ( evt.ScriptType != "VBAInterop" ) + return; + lang::EventObject aEvent; + evt.Arguments[ 0 ] >>= aEvent; + OUString sName = "UserForm"; + + uno::Reference< awt::XDialog > xDlg( aEvent.Source, uno::UNO_QUERY ); + if ( !xDlg.is() ) + { + // evt.Source is + // a) Dialog + // b) xShapeControl ( from api (sheet control) ) + // c) eventmanager ( I guess ) + // d) vba control ( from api also ) + uno::Reference< drawing::XControlShape > xCntrlShape( evt.Source, uno::UNO_QUERY ); + uno::Reference< awt::XControl > xControl( aEvent.Source, uno::UNO_QUERY ); + if ( xCntrlShape.is() ) + { + // for sheet controls ( that fire from the api ) we don't + // have the real control ( that's only available from the view ) + // api code creates just a control instance that is transferred + // via aEvent.Arguments[ 0 ] that control though has no + // info like name etc. + uno::Reference< container::XNamed > xName( xCntrlShape->getControl(), uno::UNO_QUERY_THROW ); + sName = xName->getName(); + } + else + { + // Userform control ( fired from the api or from event manager ) + uno::Reference< beans::XPropertySet > xProps; + xProps.set( xControl->getModel(), uno::UNO_QUERY_THROW ); + xProps->getPropertyValue("Name") >>= sName; + } + } + //dumpEvent( evt ); + EventInfoHash& infos = getEventTransInfo(); + EventInfoHash::const_iterator eventInfo_it = infos.find( evt.MethodName ); + EventInfoHash::const_iterator it_end = infos.end(); + if ( eventInfo_it == it_end ) + { + SAL_WARN("scripting", "Bogus event for " << evt.ScriptType ); + return; + } + + uno::Reference< script::provider::XScriptProviderSupplier > xSPS( m_xModel, uno::UNO_QUERY ); + uno::Reference< script::provider::XScriptProvider > xScriptProvider; + if ( xSPS.is() ) + { + xScriptProvider = xSPS->getScriptProvider(); + } + if ( !(xScriptProvider.is() && mpShell) ) + return; + + BasicManager* pBasicManager = mpShell->GetBasicManager(); + OUString sProject; + OUString sScriptCode( evt.ScriptCode ); + // dialogs pass their own library, presence of Dot determines that + if ( sScriptCode.indexOf( '.' ) == -1 ) + { + //'Project' is a better default but I want to force failures + //OUString sMacroLoc("Project"); + sProject = "Standard"; + + if (!pBasicManager->GetName().isEmpty()) + { + sProject = pBasicManager->GetName(); + } + } + else + { + sal_Int32 nIndex = sScriptCode.indexOf( '.' ); + sProject = sScriptCode.copy( 0, nIndex ); + sScriptCode = sScriptCode.copy( nIndex + 1 ); + } + OUString sMacroLoc = sProject + "." + sScriptCode + "."; + + for (const auto& rTxInfo : eventInfo_it->second) + { + // If the document is closed, we should not execute macro. + if (m_bDocClosed) + { + break; + } + + // see if we have a match for the handlerextension + // where ScriptCode is methodname_handlerextension + OUString sToResolve = sMacroLoc + sName + rTxInfo.sVBAName; + + ooo::vba::MacroResolvedInfo aMacroResolvedInfo = ooo::vba::resolveVBAMacro( mpShell, sToResolve ); + if ( aMacroResolvedInfo.mbFound ) + { + + if (! rTxInfo.ApproveRule(evt, rTxInfo.pPara) ) + { + continue; + } + + // !! translate arguments & emulate events where necessary + Sequence< Any > aArguments; + if ( rTxInfo.toVBA ) + { + aArguments = rTxInfo.toVBA( evt.Arguments ); + } + else + { + aArguments = evt.Arguments; + } + if ( aArguments.hasElements() ) + { + // call basic event handlers for event + + // create script url + OUString url = aMacroResolvedInfo.msResolvedMacro; + try + { + uno::Any aDummyCaller( OUString("Error") ); + if ( pRet ) + { + ooo::vba::executeMacro( mpShell, url, aArguments, *pRet, aDummyCaller ); + } + else + { + uno::Any aRet; + ooo::vba::executeMacro( mpShell, url, aArguments, aRet, aDummyCaller ); + } + } + catch ( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("scripting", "event script raised" ); + } + } + } + } +} + +namespace { + +class VBAToOOEventDescGen : public ::cppu::WeakImplHelper< XVBAToOOEventDescGen, css::lang::XServiceInfo > +{ +public: + VBAToOOEventDescGen(); + + // XVBAToOOEventDescGen + virtual Sequence< ScriptEventDescriptor > SAL_CALL getEventDescriptions( const OUString& sCtrlServiceName, const OUString& sCodeName ) override; + virtual Reference< XScriptEventsSupplier > SAL_CALL getEventSupplier( const Reference< XInterface >& xControl, const OUString& sCodeName ) override; + + OUString SAL_CALL getImplementationName() override + { + return "ooo.vba.VBAToOOEventDesc"; + } + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { getImplementationName() }; + } + +}; + +} + +VBAToOOEventDescGen::VBAToOOEventDescGen() {} + +Sequence< ScriptEventDescriptor > SAL_CALL +VBAToOOEventDescGen::getEventDescriptions( const OUString& sCntrlServiceName, const OUString& sCodeName ) +{ + ScriptEventHelper evntHelper( sCntrlServiceName ); + return evntHelper.createEvents( sCodeName ); +} + +Reference< XScriptEventsSupplier > SAL_CALL +VBAToOOEventDescGen::getEventSupplier( const Reference< XInterface >& xControl, const OUString& sCodeName ) +{ + ScriptEventHelper evntHelper( xControl ); + Reference< XScriptEventsSupplier > xSupplier = + new ReadOnlyEventsSupplier( + evntHelper.getEventListeners(), sCodeName ) ; + return xSupplier; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ooo_vba_EventListener_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new EventListener); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ooo_vba_VBAToOOEventDesc_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VBAToOOEventDescGen); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/vbaevents/vbaevents.component b/scripting/source/vbaevents/vbaevents.component new file mode 100644 index 000000000..75fcf0230 --- /dev/null +++ b/scripting/source/vbaevents/vbaevents.component @@ -0,0 +1,30 @@ +<?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="ooo.vba.EventListener" + constructor="ooo_vba_EventListener_get_implementation"> + <service name="ooo.vba.EventListener"/> + </implementation> + <implementation name="ooo.vba.VBAToOOEventDesc" + constructor="ooo_vba_VBAToOOEventDesc_get_implementation"> + <service name="ooo.vba.VBAToOOEventDesc"/> + </implementation> +</component> diff --git a/scripting/util/scriptframe.component b/scripting/util/scriptframe.component new file mode 100644 index 000000000..fc2af2a6b --- /dev/null +++ b/scripting/util/scriptframe.component @@ -0,0 +1,42 @@ +<?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.script.browse.BrowseNodeFactory" + constructor="scripting_BrowseNodeFactoryImpl_get_implementation"> + <service name="com.sun.star.script.browse.BrowseNodeFactory"/> + <singleton name="com.sun.star.script.browse.theBrowseNodeFactory"/> + </implementation> + <implementation name="com.sun.star.script.provider.MasterScriptProvider" + constructor="scripting_MasterScriptProvider_get_implementation"> + <service name="com.sun.star.script.browse.BrowseNode"/> + <service name="com.sun.star.script.provider.MasterScriptProvider"/> + <service name="com.sun.star.script.provider.ScriptProvider"/> + </implementation> + <implementation name="com.sun.star.script.provider.MasterScriptProviderFactory" + constructor="scripting_MasterScriptProviderFactory_get_implementation"> + <service name="com.sun.star.script.provider.MasterScriptProviderFactory"/> + <singleton name="com.sun.star.script.provider.theMasterScriptProviderFactory"/> + </implementation> + <implementation name="com.sun.star.script.provider.ScriptURIHelper" + constructor="scripting_ScriptingFrameworkURIHelper_get_implementation"> + <service name="com.sun.star.script.provider.ScriptURIHelper"/> + </implementation> +</component> diff --git a/scripting/workben/bindings/Highlight.xdl b/scripting/workben/bindings/Highlight.xdl new file mode 100644 index 000000000..d711d87b6 --- /dev/null +++ b/scripting/workben/bindings/Highlight.xdl @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd"> +<!-- + * 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 . +--> +<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="Highlight" dlg:left="68" dlg:top="23" dlg:width="135" dlg:height="48" dlg:closeable="true" dlg:moveable="true"> + <dlg:bulletinboard> + <dlg:text dlg:id="Label1" dlg:tab-index="2" dlg:left="3" dlg:top="8" dlg:width="33" dlg:height="9" dlg:value="Find What:"/> + <dlg:textfield dlg:id="HighlightTextField" dlg:tab-index="3" dlg:left="39" dlg:top="6" dlg:width="93" dlg:height="12"/> + <dlg:button dlg:id="HighlightButton" dlg:tab-index="0" dlg:left="20" dlg:top="24" dlg:width="46" dlg:height="18" dlg:value="Highlight"> + <script:event script:event-name="on-performaction" script:macro-name="vnd.sun.star.script:Highlight.ButtonPressHandler.bsh?language=BeanShell&location=share" script:language="Script"/> + </dlg:button> + <dlg:button dlg:id="ExitButton" dlg:tab-index="1" dlg:left="72" dlg:top="24" dlg:width="46" dlg:height="18" dlg:value="Exit"> + <script:event script:event-name="on-performaction" script:macro-name="vnd.sun.star.script:Highlight.ButtonPressHandler.bsh?language=BeanShell&location=share" script:language="Script"/> + </dlg:button> + </dlg:bulletinboard> +</dlg:window> diff --git a/scripting/workben/bindings/dialog.xlb b/scripting/workben/bindings/dialog.xlb new file mode 100644 index 000000000..1445c98b8 --- /dev/null +++ b/scripting/workben/bindings/dialog.xlb @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="ScriptBindingLibrary" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="Highlight"/> + <library:element library:name="MacroEditor"/> +</library:library> diff --git a/scripting/workben/bindings/script.xlb b/scripting/workben/bindings/script.xlb new file mode 100644 index 000000000..33eb114b5 --- /dev/null +++ b/scripting/workben/bindings/script.xlb @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="ScriptBindingLibrary" library:readonly="false" library:passwordprotected="false"> +</library:library> |