summaryrefslogtreecommitdiffstats
path: root/framework/source/recording
diff options
context:
space:
mode:
Diffstat (limited to 'framework/source/recording')
-rw-r--r--framework/source/recording/dispatchrecorder.cxx429
-rw-r--r--framework/source/recording/dispatchrecordersupplier.cxx160
2 files changed, 589 insertions, 0 deletions
diff --git a/framework/source/recording/dispatchrecorder.cxx b/framework/source/recording/dispatchrecorder.cxx
new file mode 100644
index 000000000..647b2601d
--- /dev/null
+++ b/framework/source/recording/dispatchrecorder.cxx
@@ -0,0 +1,429 @@
+/* -*- 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 <recording/dispatchrecorder.hxx>
+#include <com/sun/star/frame/DispatchStatement.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <typelib/typedescription.h>
+
+using namespace ::com::sun::star::uno;
+
+namespace framework{
+
+// used to mark a dispatch as comment (mostly it indicates an error) Changing of this define will impact all using of such comments...
+#define REM_AS_COMMENT "rem "
+
+// XInterface, XTypeProvider, XServiceInfo
+
+DEFINE_XSERVICEINFO_MULTISERVICE_2(
+ DispatchRecorder,
+ ::cppu::OWeakObject,
+ "com.sun.star.frame.DispatchRecorder",
+ "com.sun.star.comp.framework.DispatchRecorder")
+
+DEFINE_INIT_SERVICE(
+ DispatchRecorder,
+ {
+ }
+)
+
+
+static void flatten_struct_members(
+ ::std::vector< Any > * vec, void const * data,
+ typelib_CompoundTypeDescription * pTD )
+{
+ if (pTD->pBaseTypeDescription)
+ {
+ flatten_struct_members( vec, data, pTD->pBaseTypeDescription );
+ }
+ for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos )
+ {
+ vec->push_back(
+ Any( static_cast<char const *>(data) + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) );
+ }
+}
+
+static Sequence< Any > make_seq_out_of_struct(
+ Any const & val )
+{
+ Type const & type = val.getValueType();
+ TypeClass eTypeClass = type.getTypeClass();
+ if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass)
+ {
+ throw RuntimeException(
+ type.getTypeName() + "is no struct or exception!" );
+ }
+ typelib_TypeDescription * pTD = nullptr;
+ TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() );
+ OSL_ASSERT( pTD );
+ if (! pTD)
+ {
+ throw RuntimeException(
+ "cannot get type descr of type " + type.getTypeName() );
+ }
+
+ ::std::vector< Any > vec;
+ vec.reserve( reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->nMembers ); // good guess
+ flatten_struct_members( &vec, val.getValue(), reinterpret_cast<typelib_CompoundTypeDescription *>(pTD) );
+ TYPELIB_DANGER_RELEASE( pTD );
+ return Sequence< Any >( vec.data(), vec.size() );
+}
+
+DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xContext )
+ : m_nRecordingID(0)
+ , m_xConverter(css::script::Converter::create(xContext))
+{
+}
+
+DispatchRecorder::~DispatchRecorder()
+{
+}
+
+// generate header
+void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& )
+{
+ /* SAFE{ */
+ /* } */
+}
+
+void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, false );
+ m_aStatements.push_back( aStatement );
+}
+
+void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ // last parameter must be set to true -> it's a comment
+ css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, true );
+ m_aStatements.push_back( aStatement );
+}
+
+void SAL_CALL DispatchRecorder::endRecording()
+{
+ SolarMutexGuard g;
+ m_aStatements.clear();
+}
+
+OUString SAL_CALL DispatchRecorder::getRecordedMacro()
+{
+ SolarMutexGuard g;
+
+ if ( m_aStatements.empty() )
+ return OUString();
+
+ OUStringBuffer aScriptBuffer;
+ aScriptBuffer.ensureCapacity(10000);
+ m_nRecordingID = 1;
+
+ aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
+ aScriptBuffer.append("rem define variables\n");
+ aScriptBuffer.append("dim document as object\n");
+ aScriptBuffer.append("dim dispatcher as object\n");
+ aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
+ aScriptBuffer.append("rem get access to the document\n");
+ aScriptBuffer.append("document = ThisComponent.CurrentController.Frame\n");
+ aScriptBuffer.append("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
+
+ for (auto const& statement : m_aStatements)
+ implts_recordMacro( statement.aCommand, statement.aArgs, statement.bIsComment, aScriptBuffer );
+ OUString sScript = aScriptBuffer.makeStringAndClear();
+ return sScript;
+}
+
+void DispatchRecorder::AppendToBuffer( const css::uno::Any& aValue, OUStringBuffer& aArgumentBuffer )
+{
+ // if value == bool
+ if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT )
+ {
+ // structs are recorded as arrays, convert to "Sequence of any"
+ Sequence< Any > aSeq = make_seq_out_of_struct( aValue );
+ aArgumentBuffer.append("Array(");
+ for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
+ {
+ AppendToBuffer( aSeq[nAny], aArgumentBuffer );
+ if ( nAny+1 < aSeq.getLength() )
+ // not last argument
+ aArgumentBuffer.append(",");
+ }
+
+ aArgumentBuffer.append(")");
+ }
+ else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE )
+ {
+ // convert to "Sequence of any"
+ css::uno::Sequence < css::uno::Any > aSeq;
+ css::uno::Any aNew;
+ try { aNew = m_xConverter->convertTo( aValue, cppu::UnoType<css::uno::Sequence < css::uno::Any >>::get() ); }
+ catch (const css::uno::Exception&) {}
+
+ aNew >>= aSeq;
+ aArgumentBuffer.append("Array(");
+ for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
+ {
+ AppendToBuffer( aSeq[nAny], aArgumentBuffer );
+ if ( nAny+1 < aSeq.getLength() )
+ // not last argument
+ aArgumentBuffer.append(",");
+ }
+
+ aArgumentBuffer.append(")");
+ }
+ else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING )
+ {
+ // strings need \"
+ OUString sVal;
+ aValue >>= sVal;
+
+ // encode non printable characters or '"' by using the CHR$ function
+ if ( !sVal.isEmpty() )
+ {
+ const sal_Unicode* pChars = sVal.getStr();
+ bool bInString = false;
+ for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ )
+ {
+ if ( pChars[nChar] < 32 || pChars[nChar] == '"' )
+ {
+ // problematic character detected
+ if ( bInString )
+ {
+ // close current string
+ aArgumentBuffer.append("\"");
+ bInString = false;
+ }
+
+ if ( nChar>0 )
+ // if this is not the first character, parts of the string have already been added
+ aArgumentBuffer.append("+");
+
+ // add the character constant
+ aArgumentBuffer.append("CHR$(");
+ aArgumentBuffer.append( static_cast<sal_Int32>(pChars[nChar]) );
+ aArgumentBuffer.append(")");
+ }
+ else
+ {
+ if ( !bInString )
+ {
+ if ( nChar>0 )
+ // if this is not the first character, parts of the string have already been added
+ aArgumentBuffer.append("+");
+
+ // start a new string
+ aArgumentBuffer.append("\"");
+ bInString = true;
+ }
+
+ aArgumentBuffer.append( pChars[nChar] );
+ }
+ }
+
+ // close string
+ if ( bInString )
+ aArgumentBuffer.append("\"");
+ }
+ else
+ aArgumentBuffer.append("\"\"");
+ }
+ else if (auto nVal = o3tl::tryAccess<sal_Unicode>(aValue))
+ {
+ // character variables are recorded as strings, back conversion must be handled in client code
+ aArgumentBuffer.append("\"");
+ if ( *nVal == '\"' )
+ // encode \" to \"\"
+ aArgumentBuffer.append(*nVal);
+ aArgumentBuffer.append(*nVal);
+ aArgumentBuffer.append("\"");
+ }
+ else
+ {
+ css::uno::Any aNew;
+ try
+ {
+ aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING );
+ }
+ catch (const css::script::CannotConvertException&) {}
+ catch (const css::uno::Exception&) {}
+ OUString sVal;
+ aNew >>= sVal;
+
+ if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM )
+ {
+ OUString aName = aValue.getValueType().getTypeName();
+ aArgumentBuffer.append( aName );
+ aArgumentBuffer.append(".");
+ }
+
+ aArgumentBuffer.append(sVal);
+ }
+}
+
+void DispatchRecorder::implts_recordMacro( const OUString& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ bool bAsComment, OUStringBuffer& aScriptBuffer )
+{
+ OUStringBuffer aArgumentBuffer(1000);
+ // this value is used to name the arrays of aArgumentBuffer
+ OUString sArrayName = "args" + OUString::number(m_nRecordingID);
+
+ aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
+
+ sal_Int32 nLength = lArguments.getLength();
+ sal_Int32 nValidArgs = 0;
+ for( sal_Int32 i=0; i<nLength; ++i )
+ {
+ if(!lArguments[i].Value.hasValue())
+ continue;
+
+ OUStringBuffer sValBuffer(100);
+ try
+ {
+ AppendToBuffer(lArguments[i].Value, sValBuffer);
+ }
+ catch(const css::uno::Exception&)
+ {
+ sValBuffer.setLength(0);
+ }
+ if (sValBuffer.isEmpty())
+ continue;
+
+ {
+ // add arg().Name
+ if(bAsComment)
+ aArgumentBuffer.append(REM_AS_COMMENT);
+ aArgumentBuffer.append (sArrayName);
+ aArgumentBuffer.append("(");
+ aArgumentBuffer.append (nValidArgs);
+ aArgumentBuffer.append(").Name = \"");
+ aArgumentBuffer.append (lArguments[i].Name);
+ aArgumentBuffer.append("\"\n");
+
+ // add arg().Value
+ if(bAsComment)
+ aArgumentBuffer.append(REM_AS_COMMENT);
+ aArgumentBuffer.append (sArrayName);
+ aArgumentBuffer.append("(");
+ aArgumentBuffer.append (nValidArgs);
+ aArgumentBuffer.append(").Value = ");
+ aArgumentBuffer.append (sValBuffer.makeStringAndClear());
+ aArgumentBuffer.append("\n");
+
+ ++nValidArgs;
+ }
+ }
+
+ // if aArgumentBuffer exist - pack it into the aScriptBuffer
+ if(nValidArgs>0)
+ {
+ if(bAsComment)
+ aScriptBuffer.append(REM_AS_COMMENT);
+ aScriptBuffer.append("dim ");
+ aScriptBuffer.append (sArrayName);
+ aScriptBuffer.append("(");
+ aScriptBuffer.append (static_cast<sal_Int32>(nValidArgs-1)); // 0 based!
+ aScriptBuffer.append(") as new com.sun.star.beans.PropertyValue\n");
+ aScriptBuffer.append (aArgumentBuffer.makeStringAndClear());
+ aScriptBuffer.append("\n");
+ }
+
+ // add code for dispatches
+ if(bAsComment)
+ aScriptBuffer.append(REM_AS_COMMENT);
+ aScriptBuffer.append("dispatcher.executeDispatch(document, \"");
+ aScriptBuffer.append(aURL);
+ aScriptBuffer.append("\", \"\", 0, ");
+ if(nValidArgs<1)
+ aScriptBuffer.append("Array()");
+ else
+ {
+ aScriptBuffer.append( sArrayName );
+ aScriptBuffer.append("()");
+ }
+ aScriptBuffer.append(")\n\n");
+
+ /* SAFE { */
+ m_nRecordingID++;
+ /* } */
+}
+
+css::uno::Type SAL_CALL DispatchRecorder::getElementType()
+{
+ return cppu::UnoType<css::frame::DispatchStatement>::get();
+}
+
+sal_Bool SAL_CALL DispatchRecorder::hasElements()
+{
+ return (! m_aStatements.empty());
+}
+
+sal_Int32 SAL_CALL DispatchRecorder::getCount()
+{
+ return m_aStatements.size();
+}
+
+css::uno::Any SAL_CALL DispatchRecorder::getByIndex(sal_Int32 idx)
+{
+ if (idx >= static_cast<sal_Int32>(m_aStatements.size())) {
+ throw css::lang::IndexOutOfBoundsException( "Dispatch recorder out of bounds" );
+ }
+
+ Any element(&m_aStatements[idx],
+ cppu::UnoType<css::frame::DispatchStatement>::get());
+
+ return element;
+}
+
+void SAL_CALL DispatchRecorder::replaceByIndex(sal_Int32 idx, const css::uno::Any& element)
+{
+ if (element.getValueType() !=
+ cppu::UnoType<css::frame::DispatchStatement>::get()) {
+ throw css::lang::IllegalArgumentException(
+ "Illegal argument in dispatch recorder",
+ Reference< XInterface >(), 2 );
+ }
+
+ if (idx >= static_cast<sal_Int32>(m_aStatements.size())) {
+ throw css::lang::IndexOutOfBoundsException(
+ "Dispatch recorder out of bounds" );
+
+ }
+
+ auto pStatement = o3tl::doAccess<css::frame::DispatchStatement>(element);
+
+ css::frame::DispatchStatement aStatement(
+ pStatement->aCommand,
+ pStatement->aTarget,
+ pStatement->aArgs,
+ pStatement->nFlags,
+ pStatement->bIsComment);
+
+ m_aStatements[idx] = aStatement;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/recording/dispatchrecordersupplier.cxx b/framework/source/recording/dispatchrecordersupplier.cxx
new file mode 100644
index 000000000..1abbb0535
--- /dev/null
+++ b/framework/source/recording/dispatchrecordersupplier.cxx
@@ -0,0 +1,160 @@
+/* -*- 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 <recording/dispatchrecordersupplier.hxx>
+#include <services.h>
+
+#include <com/sun/star/frame/XRecordableDispatch.hpp>
+
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+// XInterface, XTypeProvider
+
+DEFINE_XSERVICEINFO_MULTISERVICE(
+ DispatchRecorderSupplier,
+ ::cppu::OWeakObject,
+ "com.sun.star.frame.DispatchRecorderSupplier",
+ IMPLEMENTATIONNAME_DISPATCHRECORDERSUPPLIER)
+
+DEFINE_INIT_SERVICE(
+ DispatchRecorderSupplier,
+ {
+ /*Attention
+ I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
+ to create a new instance of this class by our own supported service factory.
+ see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further information!
+ */
+ }
+)
+
+/**
+ @short standard constructor to create instance
+ @descr Because an instance will be initialized by her interface methods
+ it's not necessary to do anything here.
+ */
+DispatchRecorderSupplier::DispatchRecorderSupplier( const css::uno::Reference< css::lang::XMultiServiceFactory >& )
+{
+}
+
+/**
+ @short standard destructor
+ @descr We are a helper and not a real service. So we don't provide
+ dispose() functionality. This supplier dies by ref count mechanism
+ and should release all internal used ones too.
+ */
+DispatchRecorderSupplier::~DispatchRecorderSupplier()
+{
+ m_xDispatchRecorder = nullptr;
+}
+
+/**
+ @short set a new dispatch recorder on this supplier
+ @descr Because there can exist more than one recorder implementations
+ (to generate java/basic/... scripts from recorded data) it must
+ be possible to set it on a supplier.
+
+ @see getDispatchRecorder()
+
+ @param xRecorder
+ the new recorder to set it
+ <br><NULL/> isn't recommended, because recording without a
+ valid recorder can't work. But it's not checked here. So user
+ of this supplier can decide that without changing this
+ implementation.
+
+ @change 09.04.2002 by Andreas Schluens
+ */
+void SAL_CALL DispatchRecorderSupplier::setDispatchRecorder( const css::uno::Reference< css::frame::XDispatchRecorder >& xRecorder )
+{
+ SolarMutexGuard g;
+ m_xDispatchRecorder=xRecorder;
+}
+
+/**
+ @short provides access to the dispatch recorder of this supplier
+ @descr Such recorder can be used outside to record dispatches.
+ But normally he is used internally only. Of course he must used
+ from outside to get the recorded data e.g. for saving it as a
+ script.
+
+ @see setDispatchRecorder()
+
+ @return the internal used dispatch recorder
+ <br>May it can be <NULL/> if no one was set before.
+
+ @change 09.04.2002 by Andreas Schluens
+ */
+css::uno::Reference< css::frame::XDispatchRecorder > SAL_CALL DispatchRecorderSupplier::getDispatchRecorder()
+{
+ SolarMutexGuard g;
+ return m_xDispatchRecorder;
+}
+
+/**
+ @short execute a dispatch request and record it
+ @descr If given dispatch object provides right recording interface it
+ will be used. If it's not supported it record the pure dispatch
+ parameters only. There is no code neither the possibility to
+ check if recording is enabled or not.
+
+ @param aURL the command URL
+ @param lArguments optional arguments (see com.sun.star.document.MediaDescriptor for further information)
+ @param xDispatcher the original dispatch object which should be recorded
+
+ @change 09.04.2002 by Andreas Schluens
+ */
+void SAL_CALL DispatchRecorderSupplier::dispatchAndRecord( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ,
+ const css::uno::Reference< css::frame::XDispatch >& xDispatcher )
+{
+ SolarMutexClearableGuard aReadLock;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder = m_xDispatchRecorder;
+ aReadLock.clear();
+
+ // clear unspecific situations
+ if (!xDispatcher.is())
+ throw css::uno::RuntimeException("specification violation: dispatcher is NULL", static_cast< ::cppu::OWeakObject* >(this));
+
+ if (!xRecorder.is())
+ throw css::uno::RuntimeException("specification violation: no valid dispatch recorder available", static_cast< ::cppu::OWeakObject* >(this));
+
+ // check, if given dispatch supports record functionality by itself ...
+ // or must be wrapped.
+ css::uno::Reference< css::frame::XRecordableDispatch > xRecordable(
+ xDispatcher,
+ css::uno::UNO_QUERY);
+
+ if (xRecordable.is())
+ xRecordable->dispatchAndRecord(aURL,lArguments,xRecorder);
+ else
+ {
+ // There is no reason to wait for information about success
+ // of this request. Because status information of a dispatch
+ // are not guaranteed. So we execute it and record used
+ // parameters only.
+ xDispatcher->dispatch(aURL,lArguments);
+ xRecorder->recordDispatch(aURL,lArguments);
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */