summaryrefslogtreecommitdiffstats
path: root/forms/source/xforms
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /forms/source/xforms
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.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 'forms/source/xforms')
-rw-r--r--forms/source/xforms/NameContainer.hxx159
-rw-r--r--forms/source/xforms/binding.cxx1279
-rw-r--r--forms/source/xforms/binding.hxx407
-rw-r--r--forms/source/xforms/boolexpression.cxx42
-rw-r--r--forms/source/xforms/boolexpression.hxx44
-rw-r--r--forms/source/xforms/collection.hxx280
-rw-r--r--forms/source/xforms/computedexpression.cxx182
-rw-r--r--forms/source/xforms/computedexpression.hxx121
-rw-r--r--forms/source/xforms/convert.cxx335
-rw-r--r--forms/source/xforms/convert.hxx78
-rw-r--r--forms/source/xforms/datatyperepository.cxx295
-rw-r--r--forms/source/xforms/datatyperepository.hxx83
-rw-r--r--forms/source/xforms/datatypes.cxx976
-rw-r--r--forms/source/xforms/datatypes.hxx393
-rw-r--r--forms/source/xforms/enumeration.cxx59
-rw-r--r--forms/source/xforms/enumeration.hxx50
-rw-r--r--forms/source/xforms/evaluationcontext.hxx57
-rw-r--r--forms/source/xforms/mip.cxx98
-rw-r--r--forms/source/xforms/mip.hxx102
-rw-r--r--forms/source/xforms/model.cxx619
-rw-r--r--forms/source/xforms/model.hxx372
-rw-r--r--forms/source/xforms/model_helper.hxx146
-rw-r--r--forms/source/xforms/model_ui.cxx1007
-rw-r--r--forms/source/xforms/namedcollection.hxx111
-rw-r--r--forms/source/xforms/pathexpression.cxx113
-rw-r--r--forms/source/xforms/pathexpression.hxx71
-rw-r--r--forms/source/xforms/propertysetbase.cxx158
-rw-r--r--forms/source/xforms/propertysetbase.hxx340
-rw-r--r--forms/source/xforms/resourcehelper.cxx65
-rw-r--r--forms/source/xforms/resourcehelper.hxx41
-rw-r--r--forms/source/xforms/submission.cxx609
-rw-r--r--forms/source/xforms/submission.hxx232
-rw-r--r--forms/source/xforms/submission/replace.cxx131
-rw-r--r--forms/source/xforms/submission/serialization.hxx60
-rw-r--r--forms/source/xforms/submission/serialization_app_xml.cxx126
-rw-r--r--forms/source/xforms/submission/serialization_app_xml.hxx40
-rw-r--r--forms/source/xforms/submission/serialization_urlencoded.cxx181
-rw-r--r--forms/source/xforms/submission/serialization_urlencoded.hxx47
-rw-r--r--forms/source/xforms/submission/submission.hxx137
-rw-r--r--forms/source/xforms/submission/submission_get.cxx103
-rw-r--r--forms/source/xforms/submission/submission_get.hxx33
-rw-r--r--forms/source/xforms/submission/submission_post.cxx78
-rw-r--r--forms/source/xforms/submission/submission_post.hxx32
-rw-r--r--forms/source/xforms/submission/submission_put.cxx67
-rw-r--r--forms/source/xforms/submission/submission_put.hxx33
-rw-r--r--forms/source/xforms/unohelper.cxx73
-rw-r--r--forms/source/xforms/unohelper.hxx41
-rw-r--r--forms/source/xforms/xforms_services.cxx59
-rw-r--r--forms/source/xforms/xformsevent.cxx83
-rw-r--r--forms/source/xforms/xformsevent.hxx68
-rw-r--r--forms/source/xforms/xmlhelper.cxx133
-rw-r--r--forms/source/xforms/xmlhelper.hxx40
-rw-r--r--forms/source/xforms/xpathlib/extension.cxx74
-rw-r--r--forms/source/xforms/xpathlib/extension.hxx52
-rw-r--r--forms/source/xforms/xpathlib/xpathlib.cxx539
-rw-r--r--forms/source/xforms/xpathlib/xpathlib.hxx74
56 files changed, 11228 insertions, 0 deletions
diff --git a/forms/source/xforms/NameContainer.hxx b/forms/source/xforms/NameContainer.hxx
new file mode 100644
index 000000000..7c1b50993
--- /dev/null
+++ b/forms/source/xforms/NameContainer.hxx
@@ -0,0 +1,159 @@
+/* -*- 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 <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <map>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <osl/diagnose.h>
+
+typedef cppu::WeakImplHelper<
+ css::container::XNameContainer
+> NameContainer_t;
+
+template<class T>
+class NameContainer : public NameContainer_t
+{
+ typedef std::map<OUString,T> map_t;
+ map_t maItems;
+
+protected:
+ typename map_t::const_iterator findItem( const OUString& rName )
+ {
+ return maItems.find( rName );
+ }
+
+ bool hasItem( const OUString& rName )
+ {
+ return findItem( rName ) != maItems.end();
+ }
+
+ void replace( const OUString& rName,
+ const T& aElement )
+ {
+ OSL_ENSURE( hasItem( rName ), "unknown item" );
+ maItems[ rName ] = aElement;
+ }
+
+ void insert( const OUString& rName,
+ const T& aElement )
+ {
+ OSL_ENSURE( ! hasItem( rName ), "item already in set" );
+ maItems[ rName ] = aElement;
+ }
+
+ void remove( const OUString& rName )
+ {
+ OSL_ENSURE( hasItem( rName ), "item not in set" );
+ maItems.erase( rName );
+ }
+
+
+public:
+
+ NameContainer() {}
+
+
+ // methods for XElementAccess
+
+
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<T>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return ! maItems.empty();
+ }
+
+
+ // methods for XNameAccess (inherits XElementAccess)
+
+
+ virtual css::uno::Any SAL_CALL getByName(
+ const OUString& rName ) override
+ {
+ typename map_t::const_iterator aIter = findItem( rName );
+ if( aIter == maItems.end() )
+ throw css::container::NoSuchElementException();
+ return css::uno::Any( aIter->second );
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames() override
+ {
+ return comphelper::mapKeysToSequence(maItems);
+ }
+
+ virtual sal_Bool SAL_CALL hasByName(
+ const OUString& rName ) override
+ {
+ return hasItem( rName );
+ }
+
+
+ // methods for XNameReplace (inherits XNameAccess)
+
+
+ virtual void SAL_CALL replaceByName(
+ const OUString& rName,
+ const css::uno::Any& aElement ) override
+ {
+ T aItem;
+ if( !(aElement >>= aItem) )
+ throw css::lang::IllegalArgumentException();
+ if( !hasByName( rName ) )
+ throw css::container::NoSuchElementException();
+ replace( rName, aItem );
+ }
+
+
+ // methods for XNameContainer (inherits XNameReplace)
+
+
+ virtual void SAL_CALL insertByName(
+ const OUString& rName,
+ const css::uno::Any& aElement ) override
+ {
+ T aItem;
+ if( !(aElement >>= aItem) )
+ throw css::lang::IllegalArgumentException();
+ if( hasByName( rName ) )
+ throw css::container::ElementExistException();
+ insert( rName, aItem );
+ }
+
+ virtual void SAL_CALL removeByName(
+ const OUString& rName ) override
+ {
+ if( !hasByName( rName ) )
+ throw css::container::NoSuchElementException();
+ remove( rName );
+ }
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/binding.cxx b/forms/source/xforms/binding.cxx
new file mode 100644
index 000000000..32eeefc75
--- /dev/null
+++ b/forms/source/xforms/binding.cxx
@@ -0,0 +1,1279 @@
+/* -*- 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 "binding.hxx"
+
+#include "model.hxx"
+#include "unohelper.hxx"
+#include "NameContainer.hxx"
+#include "evaluationcontext.hxx"
+#include "convert.hxx"
+#include "resourcehelper.hxx"
+#include "xmlhelper.hxx"
+#include "xformsevent.hxx"
+#include <strings.hrc>
+
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <functional>
+
+#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
+#include <com/sun/star/form/binding/InvalidBindingStateException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/dom/events/XEventTarget.hpp>
+#include <com/sun/star/xml/dom/events/XEventListener.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+#include <comphelper/servicehelper.hxx>
+
+using namespace com::sun::star::xml::xpath;
+using namespace com::sun::star::xml::dom::events;
+
+using std::vector;
+using xforms::Binding;
+using xforms::MIP;
+using xforms::Model;
+using xforms::getResource;
+using xforms::EvaluationContext;
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::container::XNameAccess;
+using com::sun::star::form::binding::IncompatibleTypesException;
+using com::sun::star::form::binding::InvalidBindingStateException;
+using com::sun::star::form::binding::XValueBinding;
+using com::sun::star::lang::EventObject;
+using com::sun::star::lang::IndexOutOfBoundsException;
+using com::sun::star::lang::XUnoTunnel;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::UNO_QUERY_THROW;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::Exception;
+using com::sun::star::util::XModifyListener;
+using com::sun::star::xforms::XDataTypeRepository;
+using com::sun::star::xml::dom::NodeType_ATTRIBUTE_NODE;
+using com::sun::star::xml::dom::NodeType_TEXT_NODE;
+using com::sun::star::xml::dom::XNode;
+using com::sun::star::xml::dom::XNodeList;
+using com::sun::star::xml::dom::events::XEventListener;
+using com::sun::star::xml::dom::events::XEventTarget;
+using com::sun::star::xsd::XDataType;
+
+#define HANDLE_BindingID 0
+#define HANDLE_BindingExpression 1
+#define HANDLE_Model 2
+#define HANDLE_ModelID 3
+#define HANDLE_BindingNamespaces 4
+#define HANDLE_ReadonlyExpression 5
+#define HANDLE_RelevantExpression 6
+#define HANDLE_RequiredExpression 7
+#define HANDLE_ConstraintExpression 8
+#define HANDLE_CalculateExpression 9
+#define HANDLE_Type 10
+#define HANDLE_ReadOnly 11 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
+#define HANDLE_Relevant 12 // from com.sun.star.form.binding.ValueBinding, for interaction with a bound form control
+#define HANDLE_ModelNamespaces 13
+#define HANDLE_ExternalData 14
+
+
+Binding::Binding() :
+ mxNamespaces( new NameContainer<OUString>() ),
+ mbInCalculate( false ),
+ mnDeferModifyNotifications( 0 ),
+ mbValueModified( false ),
+ mbBindingModified( false )
+{
+ initializePropertySet();
+}
+
+Binding::~Binding()
+{
+ _setModel(nullptr);
+}
+
+void Binding::_setModel( const css::uno::Reference<css::xforms::XModel>& xModel )
+{
+ PropertyChangeNotifier aNotifyModelChange( *this, HANDLE_Model );
+ PropertyChangeNotifier aNotifyModelIDChange( *this, HANDLE_ModelID );
+
+ // prepare binding for removal of old model
+ clear(); // remove all cached data (e.g. XPath evaluation results)
+ css::uno::Reference<css::container::XNameContainer> xNamespaces = getModelNamespaces(); // save namespaces
+
+ mxModel = xModel;
+
+ // set namespaces (and move to model, if appropriate)
+ setBindingNamespaces( xNamespaces );
+ _checkBindingID();
+
+ notifyAndCachePropertyValue( HANDLE_ExternalData );
+}
+
+
+OUString Binding::getModelID() const
+{
+ Model* pModel = getModelImpl();
+ return ( pModel == nullptr ) ? OUString() : pModel->getID();
+}
+
+
+css::uno::Reference<css::xml::dom::XNodeList> Binding::getXNodeList()
+{
+ // first make sure we are bound
+ if( ! maBindingExpression.hasValue() )
+ bind();
+
+ return maBindingExpression.getXNodeList();
+}
+
+bool Binding::isSimpleBinding() const
+{
+ return maBindingExpression.isSimpleExpression()
+ && maReadonly.isSimpleExpression()
+ && maRelevant.isSimpleExpression()
+ && maRequired.isSimpleExpression()
+ && maConstraint.isSimpleExpression()
+ && maCalculate.isSimpleExpression();
+}
+
+bool Binding::isSimpleBindingExpression() const
+{
+ return maBindingExpression.isSimpleExpression();
+}
+
+void Binding::update()
+{
+ // clear all expressions (to remove cached node references)
+ maBindingExpression.clear();
+ maReadonly.clear();
+ maRelevant.clear();
+ maRequired.clear();
+ maConstraint.clear();
+ maCalculate.clear();
+
+ // let's just pretend the binding has been modified -> full rebind()
+ bindingModified();
+}
+
+void Binding::deferNotifications( bool bDefer )
+{
+ mnDeferModifyNotifications += ( bDefer ? 1 : -1 );
+ OSL_ENSURE( mnDeferModifyNotifications >= 0, "you're deferring too much" );
+
+ if( mnDeferModifyNotifications == 0 )
+ {
+ if( mbBindingModified )
+ bindingModified();
+ if( mbValueModified )
+ valueModified();
+ }
+
+ OSL_ENSURE( ( mnDeferModifyNotifications > 0 )
+ || ( ! mbBindingModified && ! mbValueModified ),
+ "deferred modifications not delivered?" );
+}
+
+bool Binding::isValid() const
+{
+ // TODO: determine whether node is suitable, not just whether it exists
+ return maBindingExpression.getNode().is() &&
+ isValid_DataType() &&
+ maMIP.isConstraint() &&
+ ( ! maMIP.isRequired() ||
+ ( maBindingExpression.hasValue() &&
+ !maBindingExpression.getString().isEmpty() ) );
+}
+
+bool Binding::isUseful() const
+{
+ // we are useful, if
+ // 0) we don't have a model
+ // (at least, in this case we shouldn't be removed from the model)
+ // 1) we have a proper name
+ // 2) we have some MIPs,
+ // 3) we are bound to some control
+ // (this can be assumed if some listeners are set)
+ bool bUseful =
+ getModelImpl() == nullptr
+// || msBindingID.getLength() > 0
+ || ! msTypeName.isEmpty()
+ || ! maReadonly.isEmptyExpression()
+ || ! maRelevant.isEmptyExpression()
+ || ! maRequired.isEmptyExpression()
+ || ! maConstraint.isEmptyExpression()
+ || ! maCalculate.isEmptyExpression()
+ || ! maModifyListeners.empty()
+ || ! maListEntryListeners.empty()
+ || ! maValidityListeners.empty();
+
+ return bUseful;
+}
+
+OUString Binding::explainInvalid()
+{
+ OUString sReason;
+ if( ! maBindingExpression.getNode().is() )
+ {
+ sReason = ( maBindingExpression.getExpression().isEmpty() )
+ ? getResource( RID_STR_XFORMS_NO_BINDING_EXPRESSION )
+ : getResource( RID_STR_XFORMS_INVALID_BINDING_EXPRESSION );
+ }
+ else if( ! isValid_DataType() )
+ {
+ sReason = explainInvalid_DataType();
+ if( sReason.isEmpty() )
+ {
+ // no explanation given by data type? Then give generic message
+ sReason = getResource( RID_STR_XFORMS_INVALID_VALUE,
+ maMIP.getTypeName() );
+ }
+ }
+ else if( ! maMIP.isConstraint() )
+ {
+ sReason = maMIP.getConstraintExplanation();
+ }
+ else if( maMIP.isRequired() && maBindingExpression.hasValue() &&
+ maBindingExpression.getString().isEmpty() )
+ {
+ sReason = getResource( RID_STR_XFORMS_REQUIRED );
+ }
+ // else: no explanation given; should only happen if data is valid
+
+ OSL_ENSURE( sReason.isEmpty() == isValid(),
+ "invalid data should have an explanation!" );
+
+ return sReason;
+}
+
+
+EvaluationContext Binding::getEvaluationContext() const
+{
+ OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
+ EvaluationContext aContext = getModelImpl()->getEvaluationContext();
+ aContext.mxNamespaces = getBindingNamespaces();
+ return aContext;
+}
+
+::std::vector<EvaluationContext> Binding::getMIPEvaluationContexts()
+{
+ OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
+
+ // bind (in case we were not bound before)
+ bind();
+ return _getMIPEvaluationContexts();
+}
+
+
+css::uno::Sequence<sal_Int8> Binding::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit aImplementationId;
+ return aImplementationId.getSeq();
+}
+
+
+void Binding::setBindingID( const OUString& sBindingID )
+{
+ msBindingID = sBindingID;
+}
+
+OUString Binding::getBindingExpression() const
+{
+ return maBindingExpression.getExpression();
+}
+
+void Binding::setBindingExpression( const OUString& sBindingExpression)
+{
+ maBindingExpression.setExpression( sBindingExpression );
+ bindingModified();
+}
+
+OUString Binding::getReadonlyExpression() const
+{
+ return maReadonly.getExpression();
+}
+
+void Binding::setReadonlyExpression( const OUString& sReadonly)
+{
+ maReadonly.setExpression( sReadonly );
+ bindingModified();
+}
+
+OUString Binding::getRelevantExpression() const
+{
+ return maRelevant.getExpression();
+}
+
+void Binding::setRelevantExpression( const OUString& sRelevant )
+{
+ maRelevant.setExpression( sRelevant );
+ bindingModified();
+}
+
+OUString Binding::getRequiredExpression() const
+{
+ return maRequired.getExpression();
+}
+
+void Binding::setRequiredExpression( const OUString& sRequired )
+{
+ maRequired.setExpression( sRequired );
+ bindingModified();
+}
+
+OUString Binding::getConstraintExpression() const
+{
+ return maConstraint.getExpression();
+}
+
+void Binding::setConstraintExpression( const OUString& sConstraint )
+{
+ maConstraint.setExpression( sConstraint );
+ msExplainConstraint = getResource( RID_STR_XFORMS_INVALID_CONSTRAINT,
+ sConstraint );
+
+ // TODO: This should only re-evaluate the constraint, and notify
+ // the validity constraint listeners; instead we currently pretend
+ // the entire binding was notified, which does a little too much.
+ bindingModified();
+}
+
+OUString Binding::getCalculateExpression() const
+{
+ return maCalculate.getExpression();
+}
+
+void Binding::setCalculateExpression( const OUString& sCalculate )
+{
+ maCalculate.setExpression( sCalculate );
+ bindingModified();
+}
+
+
+void Binding::setType( const OUString& sTypeName )
+{
+ msTypeName = sTypeName;
+ bindingModified();
+}
+
+void Binding::setBindingNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces )
+{
+ _setNamespaces( rNamespaces, true );
+}
+
+css::uno::Reference<css::container::XNameContainer> Binding::getModelNamespaces() const
+{
+ return _getNamespaces();
+}
+
+void Binding::setModelNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces )
+{
+ _setNamespaces( rNamespaces, false );
+}
+
+bool Binding::getReadOnly() const
+{
+ return maMIP.isReadonly();
+}
+
+bool Binding::getRelevant() const
+{
+ return maMIP.isRelevant();
+}
+
+bool Binding::getExternalData() const
+{
+ bool bExternalData = true;
+ if ( !mxModel.is() )
+ return bExternalData;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( mxModel, UNO_QUERY_THROW );
+ OSL_VERIFY(
+ xModelProps->getPropertyValue( "ExternalData" ) >>= bExternalData );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.xforms");
+ }
+ return bExternalData;
+}
+
+
+void Binding::checkLive()
+{
+ if( ! isLive() )
+ throw RuntimeException("Binding not initialized", static_cast<XValueBinding*>(this));
+}
+
+bool Binding::isLive() const
+{
+ const Model* pModel = getModelImpl();
+ return pModel && pModel->isInitialized();
+}
+
+Model* Binding::getModelImpl() const
+{
+ return comphelper::getFromUnoTunnel<Model>( mxModel );
+}
+
+static void lcl_addListenerToNode( const Reference<XNode>& xNode,
+ const Reference<XEventListener>& xListener )
+{
+ Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
+ if( !xTarget.is() )
+ return;
+
+ xTarget->addEventListener( "DOMCharacterDataModified",
+ xListener, false );
+ xTarget->addEventListener( "DOMCharacterDataModified",
+ xListener, true );
+ xTarget->addEventListener( "DOMAttrModified",
+ xListener, false );
+ xTarget->addEventListener( "DOMAttrModified",
+ xListener, true );
+ xTarget->addEventListener( "DOMAttrModified",
+ xListener, true );
+ xTarget->addEventListener( "xforms-generic",
+ xListener, true );
+}
+
+static void lcl_removeListenerFromNode( const Reference<XNode>& xNode,
+ const Reference<XEventListener>& xListener )
+{
+ Reference<XEventTarget> xTarget( xNode, UNO_QUERY );
+ if( !xTarget.is() )
+ return;
+
+ xTarget->removeEventListener( "DOMCharacterDataModified",
+ xListener, false );
+ xTarget->removeEventListener( "DOMCharacterDataModified",
+ xListener, true );
+ xTarget->removeEventListener( "DOMAttrModified",
+ xListener, false );
+ xTarget->removeEventListener( "DOMAttrModified",
+ xListener, true );
+ xTarget->removeEventListener( "xforms-generic",
+ xListener, true );
+}
+
+::std::vector<EvaluationContext> Binding::_getMIPEvaluationContexts() const
+{
+ OSL_ENSURE( getModelImpl() != nullptr, "need model impl" );
+
+ // iterate over nodes of bind expression and create
+ // EvaluationContext for each
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+ ::std::vector<EvaluationContext> aVector;
+ for (auto const& node : aNodes)
+ {
+ OSL_ENSURE( node.is(), "no node?" );
+
+ // create proper evaluation context for this MIP
+ aVector.emplace_back( node, getModel(), getBindingNamespaces() );
+ }
+ return aVector;
+}
+
+void Binding::bind( bool bForceRebind )
+{
+ if( ! mxModel.is() )
+ throw RuntimeException("Binding has no Model", static_cast<XValueBinding*>(this));
+
+ // bind() will evaluate this binding as follows:
+ // 1) evaluate the binding expression
+ // 1b) if necessary, create node according to 'lazy author' rules
+ // 2) register suitable listeners on the instance (and remove old ones)
+ // 3) remove old MIPs defined by this binding
+ // 4) for every node in the binding nodeset do:
+ // 1) create proper evaluation context for this MIP
+ // 2) evaluate calculate expression (and push value into instance)
+ // 3) evaluate remaining MIPs
+ // 4) evaluate the locally defined MIPs, and push them to the model
+
+
+ // 1) evaluate the binding expression
+ EvaluationContext aContext = getEvaluationContext();
+ maBindingExpression.evaluate( aContext );
+ if( ! maBindingExpression.getNode().is() )
+ {
+ // 1b) create node (if valid element name)
+ if( isValidQName( maBindingExpression.getExpression(),
+ aContext.mxNamespaces ) )
+ {
+ aContext.mxContextNode->appendChild(
+ aContext.mxContextNode->getOwnerDocument()->createElement(
+ maBindingExpression.getExpression() ) );
+ maBindingExpression.evaluate( aContext );
+ OSL_ENSURE( maBindingExpression.getNode().is(),
+ "we should bind to the newly inserted node!" );
+ }
+ }
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+
+ // 2) register suitable listeners on the instance (and remove old ones)
+ if( maEventNodes.empty() || bForceRebind )
+ {
+ for (auto const& eventNode : maEventNodes)
+ lcl_removeListenerFromNode( eventNode, this );
+ maEventNodes.clear();
+ if( isSimpleBinding() )
+ maEventNodes.insert(maEventNodes.end(), aNodes.begin(), aNodes.end());
+ else
+ maEventNodes.emplace_back( aContext.mxContextNode->getOwnerDocument(),
+ UNO_QUERY_THROW );
+ for (auto const& eventNode : maEventNodes)
+ lcl_addListenerToNode( eventNode, this );
+ }
+
+ // 3) remove old MIPs defined by this binding
+ Model* pModel = getModelImpl();
+ OSL_ENSURE( pModel != nullptr, "need model" );
+ pModel->removeMIPs( this );
+
+ // 4) calculate all MIPs
+ ::std::vector<EvaluationContext> aMIPContexts = _getMIPEvaluationContexts();
+ for (auto & context : aMIPContexts)
+ {
+ EvaluationContext& rContext = context;
+
+ // evaluate calculate expression (and push value into instance)
+ // (prevent recursion using mbInCalculate
+ if( ! maCalculate.isEmptyExpression() )
+ {
+ if( ! mbInCalculate )
+ {
+ mbInCalculate = true;
+ maCalculate.evaluate( rContext );
+ pModel->setSimpleContent( rContext.mxContextNode,
+ maCalculate.getString() );
+ mbInCalculate = false;
+ }
+ }
+
+ // now evaluate remaining MIPs in the appropriate context
+ maReadonly.evaluate( rContext );
+ maRelevant.evaluate( rContext );
+ maRequired.evaluate( rContext );
+ maConstraint.evaluate( rContext );
+ // type is static; does not need updating
+
+ // evaluate the locally defined MIPs, and push them to the model
+ pModel->addMIP( this, rContext.mxContextNode, getLocalMIP() );
+ }
+}
+
+
+// helper for Binding::valueModified
+static void lcl_modified( const css::uno::Reference<css::util::XModifyListener>& xListener,
+ const Reference<XInterface>& xSource )
+{
+ OSL_ENSURE( xListener.is(), "no listener?" );
+ xListener->modified( EventObject( xSource ) );
+}
+
+// helper for Binding::valueModified
+static void lcl_listentry( const css::uno::Reference<css::form::binding::XListEntryListener>& xListener,
+ const Reference<XInterface>& xSource )
+{
+ OSL_ENSURE( xListener.is(), "no listener?" );
+ // TODO: send fine granular events
+ xListener->allEntriesChanged( EventObject( xSource ) );
+}
+
+// helper for Binding::valueModified
+static void lcl_validate( const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener,
+ const Reference<XInterface>& xSource )
+{
+ OSL_ENSURE( xListener.is(), "no listener?" );
+ xListener->validityConstraintChanged( EventObject( xSource ) );
+}
+
+
+void Binding::valueModified()
+{
+ // defer notifications, if so desired
+ if( mnDeferModifyNotifications > 0 )
+ {
+ mbValueModified = true;
+ return;
+ }
+ mbValueModified = false;
+
+ // query MIP used by our first node (also note validity)
+ Reference<XNode> xNode = maBindingExpression.getNode();
+ maMIP = getModelImpl()->queryMIP( xNode );
+
+ // distribute MIPs _used_ by this binding
+ if( xNode.is() )
+ {
+ notifyAndCachePropertyValue( HANDLE_ReadOnly );
+ notifyAndCachePropertyValue( HANDLE_Relevant );
+ }
+
+ // iterate over _value_ listeners and send each a modified signal,
+ // using this object as source (will also update validity, because
+ // control will query once the value has changed)
+ Reference<XInterface> xSource = static_cast<XPropertySet*>( this );
+ ::std::for_each( maModifyListeners.begin(),
+ maModifyListeners.end(),
+ ::std::bind( lcl_modified, std::placeholders::_1, xSource ) );
+ ::std::for_each( maListEntryListeners.begin(),
+ maListEntryListeners.end(),
+ ::std::bind( lcl_listentry, std::placeholders::_1, xSource ) );
+ ::std::for_each( maValidityListeners.begin(),
+ maValidityListeners.end(),
+ ::std::bind( lcl_validate, std::placeholders::_1, xSource ) );
+
+ // now distribute MIPs to children
+ if( xNode.is() )
+ distributeMIP( xNode->getFirstChild() );
+}
+
+void Binding::distributeMIP( const css::uno::Reference<css::xml::dom::XNode> & rxNode ) {
+
+ rtl::Reference<css::xforms::XFormsEventConcrete> pEvent = new css::xforms::XFormsEventConcrete;
+ pEvent->initXFormsEvent("xforms-generic", true, false);
+
+ // naive depth-first traversal
+ css::uno::Reference<css::xml::dom::XNode> xNode( rxNode );
+ while(xNode.is()) {
+
+ // notifications should be triggered at the
+ // leaf nodes first, bubbling upwards the hierarchy.
+ css::uno::Reference<css::xml::dom::XNode> child(xNode->getFirstChild());
+ if(child.is())
+ distributeMIP(child);
+
+ // we're standing at a particular node somewhere
+ // below the one which changed a property (MIP).
+ // bindings which are listening at this node will receive
+ // a notification message about what exactly happened.
+ Reference< XEventTarget > target(xNode,UNO_QUERY);
+ target->dispatchEvent(pEvent);
+
+ xNode = xNode->getNextSibling();
+ }
+}
+
+void Binding::bindingModified()
+{
+ // defer notifications, if so desired
+ if( mnDeferModifyNotifications > 0 )
+ {
+ mbBindingModified = true;
+ return;
+ }
+ mbBindingModified = false;
+
+ // rebind (if live); then call valueModified
+ // A binding should be inert until its model is fully constructed.
+ if( isLive() )
+ {
+ bind( true );
+ valueModified();
+ }
+}
+
+
+MIP Binding::getLocalMIP() const
+{
+ MIP aMIP;
+
+ if( maReadonly.hasValue() )
+ aMIP.setReadonly( maReadonly.getBool() );
+ if( maRelevant.hasValue() )
+ aMIP.setRelevant( maRelevant.getBool( true ) );
+ if( maRequired.hasValue() )
+ aMIP.setRequired( maRequired.getBool() );
+ if( maConstraint.hasValue() )
+ {
+ aMIP.setConstraint( maConstraint.getBool( true ) );
+ if( ! aMIP.isConstraint() )
+ aMIP.setConstraintExplanation( msExplainConstraint );
+ }
+ if( !msTypeName.isEmpty() )
+ aMIP.setTypeName( msTypeName );
+
+ // calculate: only handle presence of calculate; value set elsewhere
+ aMIP.setHasCalculate( !maCalculate.isEmptyExpression() );
+
+ return aMIP;
+}
+
+css::uno::Reference<css::xsd::XDataType> Binding::getDataType() const
+{
+ OSL_ENSURE( getModel().is(), "need model" );
+ OSL_ENSURE( getModel()->getDataTypeRepository().is(), "need types" );
+
+ Reference<XDataTypeRepository> xRepository =
+ getModel()->getDataTypeRepository();
+ OUString sTypeName = maMIP.getTypeName();
+
+ return ( xRepository.is() && xRepository->hasByName( sTypeName ) )
+ ? Reference<XDataType>( xRepository->getByName( sTypeName ), UNO_QUERY)
+ : Reference<XDataType>( nullptr );
+}
+
+bool Binding::isValid_DataType() const
+{
+ Reference<XDataType> xDataType = getDataType();
+ return !xDataType.is()
+ || xDataType->validate( maBindingExpression.getString() );
+}
+
+OUString Binding::explainInvalid_DataType()
+{
+ Reference<XDataType> xDataType = getDataType();
+ return xDataType.is()
+ ? xDataType->explainInvalid( maBindingExpression.getString() )
+ : OUString();
+}
+
+void Binding::clear()
+{
+ // remove MIPs contributed by this binding
+ Model* pModel = getModelImpl();
+ if( pModel != nullptr )
+ pModel->removeMIPs( this );
+
+ // remove all references
+ for (auto const& eventNode : maEventNodes)
+ lcl_removeListenerFromNode( eventNode, this );
+ maEventNodes.clear();
+
+ // clear expressions
+ maBindingExpression.clear();
+ maReadonly.clear();
+ maRelevant.clear();
+ maRequired.clear();
+ maConstraint.clear();
+ maCalculate.clear();
+
+ // TODO: what about our listeners?
+}
+
+
+static void lcl_removeOtherNamespaces( const css::uno::Reference<css::container::XNameContainer>& xFrom,
+ css::uno::Reference<css::container::XNameContainer> const & xTo )
+{
+ OSL_ENSURE( xFrom.is(), "no source" );
+ OSL_ENSURE( xTo.is(), "no target" );
+
+ // iterate over name in source
+ Sequence<OUString> aNames = xTo->getElementNames();
+ sal_Int32 nNames = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ for( sal_Int32 i = 0; i < nNames; i++ )
+ {
+ const OUString& rName = pNames[i];
+
+ if( ! xFrom->hasByName( rName ) )
+ xTo->removeByName( rName );
+ }
+}
+
+/** copy namespaces from one namespace container into another
+ * @param bOverwrite true: overwrite namespaces in target
+ * false: do not overwrite namespaces in target
+ * @param bMove true: move namespaces (i.e., delete in source)
+ * false: copy namespaces (do not modify source)
+ * @param bFromSource true: use elements from source
+ * false: use only elements from target
+ */
+static void lcl_copyNamespaces( const css::uno::Reference<css::container::XNameContainer>& xFrom,
+ css::uno::Reference<css::container::XNameContainer> const & xTo,
+ bool bOverwrite )
+{
+ OSL_ENSURE( xFrom.is(), "no source" );
+ OSL_ENSURE( xTo.is(), "no target" );
+
+ // iterate over name in source
+ Sequence<OUString> aNames = xFrom->getElementNames();
+ sal_Int32 nNames = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ for( sal_Int32 i = 0; i < nNames; i++ )
+ {
+ const OUString& rName = pNames[i];
+
+ // determine whether to copy the value, and whether to delete
+ // it in the source:
+
+ bool bInTarget = xTo->hasByName( rName );
+
+ // we copy: if property is in target, and
+ // if bOverwrite is set, or when the namespace prefix is free
+ bool bCopy = bOverwrite || ! bInTarget;
+
+ // and now... ACTION!
+ if( bCopy )
+ {
+ if( bInTarget )
+ xTo->replaceByName( rName, xFrom->getByName( rName ) );
+ else
+ xTo->insertByName( rName, xFrom->getByName( rName ) );
+ }
+ }
+}
+
+// implement get*Namespaces()
+// (identical for both variants)
+css::uno::Reference<css::container::XNameContainer> Binding::_getNamespaces() const
+{
+ css::uno::Reference<css::container::XNameContainer> xNamespaces = new NameContainer<OUString>();
+ lcl_copyNamespaces( mxNamespaces, xNamespaces, true );
+
+ // merge model's with binding's own namespaces
+ Model* pModel = getModelImpl();
+ if( pModel != nullptr )
+ lcl_copyNamespaces( pModel->getNamespaces(), xNamespaces, false );
+
+ return xNamespaces;
+}
+
+// implement set*Namespaces()
+// bBinding = true: setBindingNamespaces, otherwise: setModelNamespaces
+void Binding::_setNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces,
+ bool bBinding )
+{
+ Model* pModel = getModelImpl();
+ css::uno::Reference<css::container::XNameContainer> xModelNamespaces = ( pModel != nullptr )
+ ? pModel->getNamespaces()
+ : nullptr;
+ OSL_ENSURE( ( pModel != nullptr ) == xModelNamespaces.is(), "no model nmsp?");
+
+ // remove deleted namespaces
+ lcl_removeOtherNamespaces( rNamespaces, mxNamespaces );
+ if( !bBinding && xModelNamespaces.is() )
+ lcl_removeOtherNamespaces( rNamespaces, xModelNamespaces );
+
+ // copy namespaces as appropriate
+ Sequence<OUString> aNames = rNamespaces->getElementNames();
+ sal_Int32 nNames = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ for( sal_Int32 i = 0; i < nNames; i++ )
+ {
+ const OUString& rName = pNames[i];
+ Any aValue = rNamespaces->getByName( rName );
+
+ // determine whether the namespace should go into model's or
+ // into binding's namespaces
+ bool bLocal =
+ ! xModelNamespaces.is()
+ || mxNamespaces->hasByName( rName )
+ || ( bBinding
+ && xModelNamespaces.is()
+ && xModelNamespaces->hasByName( rName ) );
+
+ // write namespace into the appropriate namespace container
+ css::uno::Reference<css::container::XNameContainer>& rWhich = bLocal ? mxNamespaces : xModelNamespaces;
+ OSL_ENSURE( rWhich.is(), "whoops" );
+ if( rWhich->hasByName( rName ) )
+ rWhich->replaceByName( rName, aValue );
+ else
+ rWhich->insertByName( rName, aValue );
+
+ // always 'promote' namespaces from binding to model, if equal
+ if( xModelNamespaces.is()
+ && xModelNamespaces->hasByName( rName )
+ && mxNamespaces->hasByName( rName )
+ && xModelNamespaces->getByName( rName ) == mxNamespaces->getByName( rName ) )
+ {
+ mxNamespaces->removeByName( rName );
+ }
+ }
+
+ // ... done. But we modified the binding!
+ bindingModified();
+}
+
+void Binding::_checkBindingID()
+{
+ if( !getModel().is() )
+ return;
+
+ Reference<XNameAccess> xBindings( getModel()->getBindings(), UNO_QUERY_THROW );
+ if( !msBindingID.isEmpty() )
+ return;
+
+ // no binding ID? then make one up!
+ OUString sIDPrefix = getResource( RID_STR_XFORMS_BINDING_UI_NAME ) + " ";
+ sal_Int32 nNumber = 0;
+ OUString sName;
+ do
+ {
+ nNumber++;
+ sName = sIDPrefix + OUString::number( nNumber );
+ }
+ while( xBindings->hasByName( sName ) );
+ setBindingID( sName );
+}
+
+
+// XValueBinding
+
+
+css::uno::Sequence<css::uno::Type> Binding::getSupportedValueTypes()
+{
+ return Convert::get().getTypes();
+}
+
+sal_Bool Binding::supportsType( const css::uno::Type& rType )
+{
+ return Convert::get().hasType( rType );
+}
+
+css::uno::Any Binding::getValue( const css::uno::Type& rType )
+{
+ // first, check for model
+ checkLive();
+
+ // second, check for type
+ if( ! supportsType( rType ) )
+ throw IncompatibleTypesException("type unsupported", static_cast<XValueBinding*>(this));
+
+ // return string value (if present; else return empty Any)
+ css::uno::Any result;
+ if(maBindingExpression.hasValue()) {
+ OUString pathExpr(maBindingExpression.getString());
+ Convert &rConvert = Convert::get();
+ result = rConvert.toAny(pathExpr,rType);
+ }
+
+ return result;
+}
+
+void Binding::setValue( const css::uno::Any& aValue )
+{
+ // first, check for model
+ checkLive();
+
+ // check for supported type
+ if( ! supportsType( aValue.getValueType() ) )
+ throw IncompatibleTypesException("type unsupported", static_cast<XValueBinding*>(this));
+
+ if( !maBindingExpression.hasValue() )
+ throw InvalidBindingStateException("no suitable node found", static_cast<XValueBinding*>(this));
+
+ css::uno::Reference<css::xml::dom::XNode> xNode = maBindingExpression.getNode();
+ if( !xNode.is() )
+ throw InvalidBindingStateException("no suitable node found", static_cast<XValueBinding*>(this));
+
+ OUString sValue = Convert::get().toXSD( aValue );
+ bool bSuccess = getModelImpl()->setSimpleContent( xNode, sValue );
+ if( ! bSuccess )
+ throw InvalidBindingStateException("can't set value", static_cast<XValueBinding*>(this));
+
+
+}
+
+
+// XListEntry Source
+
+
+sal_Int32 Binding::getListEntryCount()
+{
+ // first, check for model
+ checkLive();
+
+ // return size of node list
+ return maBindingExpression.getNodeList().size();
+}
+
+static void lcl_getString( const Reference<XNode>& xNode, OUStringBuffer& rBuffer )
+{
+ if( xNode->getNodeType() == NodeType_TEXT_NODE
+ || xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
+ {
+ rBuffer.append( xNode->getNodeValue() );
+ }
+ else
+ {
+ for( Reference<XNode> xChild = xNode->getFirstChild();
+ xChild.is();
+ xChild = xChild->getNextSibling() )
+ {
+ lcl_getString( xChild, rBuffer );
+ }
+ }
+}
+
+static OUString lcl_getString( const Reference<XNode>& xNode )
+{
+ OUStringBuffer aBuffer;
+ lcl_getString( xNode, aBuffer );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString Binding::getListEntry( sal_Int32 nPosition )
+{
+ // first, check for model
+ checkLive();
+
+ // check bounds and return proper item
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+ if( nPosition < 0 || o3tl::make_unsigned(nPosition) >= aNodes.size() )
+ throw IndexOutOfBoundsException("", static_cast<XValueBinding*>(this));
+ return lcl_getString( aNodes[ nPosition ] );
+}
+
+Sequence<OUString> Binding::getAllListEntries()
+{
+ // first, check for model
+ checkLive();
+
+ // create sequence of string values
+ PathExpression::NodeVector_t aNodes = maBindingExpression.getNodeList();
+ Sequence<OUString> aSequence( aNodes.size() );
+ OUString* pSequence = aSequence.getArray();
+ for( sal_Int32 n = 0; n < aSequence.getLength(); n++ )
+ {
+ pSequence[n] = lcl_getString( aNodes[n] );
+ }
+
+ return aSequence;
+}
+
+void Binding::addListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>& xListener )
+{
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( ::std::find( maListEntryListeners.begin(),
+ maListEntryListeners.end(),
+ xListener)
+ == maListEntryListeners.end() )
+ maListEntryListeners.push_back( xListener );
+}
+
+void Binding::removeListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>& xListener )
+{
+ XListEntryListeners_t::iterator aIter =
+ ::std::find( maListEntryListeners.begin(), maListEntryListeners.end(),
+ xListener );
+ if( aIter != maListEntryListeners.end() )
+ maListEntryListeners.erase( aIter );
+}
+
+
+// XValidator
+
+
+sal_Bool Binding::isValid( const css::uno::Any& )
+{
+ // first, check for model
+ checkLive();
+
+ // ignore value; determine validate only on current data
+ return isValid();
+}
+
+OUString Binding::explainInvalid(
+ const css::uno::Any& /*Value*/ )
+{
+ // first, check for model
+ checkLive();
+
+ // ignore value; determine explanation only on current data
+ return explainInvalid();
+}
+
+void Binding::addValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener )
+{
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( ::std::find(maValidityListeners.begin(), maValidityListeners.end(), xListener)
+ == maValidityListeners.end() )
+ maValidityListeners.push_back( xListener );
+}
+
+void Binding::removeValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener )
+{
+ XValidityConstraintListeners_t::iterator aIter =
+ ::std::find( maValidityListeners.begin(), maValidityListeners.end(),
+ xListener );
+ if( aIter != maValidityListeners.end() )
+ maValidityListeners.erase( aIter );
+}
+
+
+// xml::dom::event::XEventListener
+
+
+void Binding::handleEvent( const css::uno::Reference<css::xml::dom::events::XEvent>& xEvent )
+{
+ OUString sType(xEvent->getType());
+ //OUString sEventMIPChanged("xforms-generic");
+ //if(sType.equals(sEventMIPChanged)) {
+ if(sType == "xforms-generic") {
+
+ // the modification of the 'mnDeferModifyNotifications'-member
+ // is necessary to prevent infinite notification looping.
+ // This can happened in case the binding which caused
+ // the notification chain is listening to those events
+ // as well...
+ bool bPreserveValueModified = mbValueModified;
+ mnDeferModifyNotifications++;
+ valueModified();
+ --mnDeferModifyNotifications;
+ mbValueModified = bPreserveValueModified;
+ return;
+ }
+
+ // if we're a dynamic binding, we better re-bind, too!
+ bind();
+
+ // our value was maybe modified
+ valueModified();
+}
+
+
+// lang::XUnoTunnel
+
+
+sal_Int64 Binding::getSomething( const css::uno::Sequence<sal_Int8>& xId )
+{
+ return comphelper::getSomethingImpl(xId, this);
+}
+
+
+// XCloneable
+
+
+css::uno::Reference<css::util::XCloneable> SAL_CALL Binding::createClone()
+{
+ Reference< XPropertySet > xClone;
+
+ Model* pModel = getModelImpl();
+ if ( pModel )
+ xClone = pModel->cloneBinding( this );
+ else
+ {
+ xClone = new Binding;
+ copy( this, xClone );
+ }
+ return css::uno::Reference<css::util::XCloneable>( xClone, UNO_QUERY );
+}
+
+
+// property set implementations
+
+void Binding::initializePropertySet()
+{
+ registerProperty( css::beans::Property("BindingID", HANDLE_BindingID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setBindingID, &Binding::getBindingID));
+
+ registerProperty( css::beans::Property("BindingExpression", HANDLE_BindingExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setBindingExpression, &Binding::getBindingExpression));
+
+ registerProperty( css::beans::Property("Model", HANDLE_Model, cppu::UnoType<css::uno::Reference<css::xforms::XModel>>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, css::uno::Reference<css::xforms::XModel> >(this, nullptr, &Binding::getModel));
+
+ registerProperty( css::beans::Property("BindingNamespaces", HANDLE_BindingNamespaces, cppu::UnoType<css::uno::Reference<css::container::XNameContainer>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, css::uno::Reference<css::container::XNameContainer> >(this, &Binding::setBindingNamespaces, &Binding::getBindingNamespaces));
+
+ registerProperty( css::beans::Property("ModelNamespaces", HANDLE_ModelNamespaces, cppu::UnoType<css::uno::Reference<css::container::XNameContainer>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, css::uno::Reference<css::container::XNameContainer> >(this, &Binding::setModelNamespaces, &Binding::getModelNamespaces));
+
+ registerProperty( css::beans::Property("ModelID", HANDLE_ModelID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, OUString >(this, nullptr, &Binding::getModelID));
+
+ registerProperty( css::beans::Property("ReadonlyExpression", HANDLE_ReadonlyExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setReadonlyExpression, &Binding::getReadonlyExpression));
+
+ registerProperty( css::beans::Property("RelevantExpression", HANDLE_RelevantExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setRelevantExpression, &Binding::getRelevantExpression));
+
+ registerProperty( css::beans::Property("RequiredExpression", HANDLE_RequiredExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setRequiredExpression, &Binding::getRequiredExpression));
+
+ registerProperty( css::beans::Property("ConstraintExpression", HANDLE_ConstraintExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setConstraintExpression, &Binding::getConstraintExpression));
+
+ registerProperty( css::beans::Property("CalculateExpression", HANDLE_CalculateExpression, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setCalculateExpression, &Binding::getCalculateExpression));
+
+ registerProperty( css::beans::Property("Type", HANDLE_Type, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Binding, OUString >(this, &Binding::setType, &Binding::getType));
+
+ registerProperty( css::beans::Property("ReadOnly", HANDLE_ReadOnly, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, bool >(this, nullptr, &Binding::getReadOnly));
+
+ registerProperty( css::beans::Property("Relevant", HANDLE_Relevant, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new DirectPropertyAccessor< Binding, bool >(this, nullptr, &Binding::getRelevant));
+
+ registerProperty( css::beans::Property("ExternalData", HANDLE_ExternalData, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
+ new BooleanPropertyAccessor< Binding >(this, nullptr, &Binding::getExternalData));
+
+ initializePropertyValueCache( HANDLE_ReadOnly );
+ initializePropertyValueCache( HANDLE_Relevant );
+ initializePropertyValueCache( HANDLE_ExternalData );
+}
+
+void Binding::addModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener )
+{
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener )
+ == maModifyListeners.end() )
+ maModifyListeners.push_back( xListener );
+
+ // HACK: currently, we have to 'push' some MIPs to the control
+ // (read-only, relevant, etc.) To enable this, we need to update
+ // the control at least once when it registers here.
+ valueModified();
+}
+
+void Binding::removeModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener )
+{
+ ModifyListeners_t::iterator aIter =
+ ::std::find( maModifyListeners.begin(), maModifyListeners.end(), xListener );
+ if( aIter != maModifyListeners.end() )
+ maModifyListeners.erase( aIter );
+}
+
+
+OUString Binding::getName()
+{
+ return getBindingID();
+}
+
+void SAL_CALL Binding::setName( const OUString& rName )
+{
+ // use the XPropertySet methods, so the change in the name is notified to the
+ // property listeners
+ setFastPropertyValue( HANDLE_BindingID, Any( rName ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/binding.hxx b/forms/source/xforms/binding.hxx
new file mode 100644
index 000000000..d7201a5b5
--- /dev/null
+++ b/forms/source/xforms/binding.hxx
@@ -0,0 +1,407 @@
+/* -*- 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/uno/Reference.hxx>
+#include <cppuhelper/implbase.hxx>
+#include "propertysetbase.hxx"
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <com/sun/star/form/validation/XValidator.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/xml/dom/events/XEventListener.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+
+#include "pathexpression.hxx"
+#include "boolexpression.hxx"
+#include "mip.hxx"
+#include <rtl/ustring.hxx>
+#include <vector>
+
+// forward declaractions
+namespace xforms
+{
+ class Model;
+ class EvaluationContext;
+}
+namespace com::sun::star {
+ namespace xml {
+ namespace xpath { class XXPathAPI; }
+ namespace dom
+ {
+ class XNode;
+ class XNodeList;
+ }
+ }
+ namespace container { class XNameContainer; }
+ namespace xforms { class XModel; }
+ namespace xsd { class XDataType; }
+}
+
+
+namespace xforms
+{
+
+/** An XForms Binding. Contains:
+ * # a connection to its model
+ * # an ID
+ * # a binding expression
+ * # model item properties
+ * # (NOT YET IMPLEMENTED) child bindings (sequence of)
+ *
+ * See http://www.w3.org/TR/xforms/ for more information.
+ */
+
+typedef cppu::ImplInheritanceHelper<
+ PropertySetBase,
+ css::form::binding::XValueBinding,
+ css::form::binding::XListEntrySource,
+ css::form::validation::XValidator,
+ css::util::XModifyBroadcaster,
+ css::container::XNamed,
+ css::xml::dom::events::XEventListener,
+ css::lang::XUnoTunnel,
+ css::util::XCloneable
+> Binding_t;
+
+class Binding : public Binding_t
+{
+public:
+ typedef std::vector<css::uno::Reference<css::util::XModifyListener> > ModifyListeners_t;
+ typedef std::vector<css::uno::Reference<css::form::validation::XValidityConstraintListener> > XValidityConstraintListeners_t;
+ typedef std::vector<css::uno::Reference<css::form::binding::XListEntryListener> > XListEntryListeners_t;
+
+
+private:
+
+ /// the Model to which this Binding belongs; may be NULL
+ css::uno::Reference<css::xforms::XModel> mxModel;
+
+ /// binding-ID. A document-wide unique ID for this binding element.
+ OUString msBindingID;
+
+ /// an XPath-expression to be instantiated on the data instance
+ PathExpression maBindingExpression;
+
+ /// an XPath-expression to determine read-only status
+ BoolExpression maReadonly;
+
+ /// an XPath-expression to determine relevance
+ BoolExpression maRelevant;
+
+ /// an XPath-expression to determine if item is required
+ BoolExpression maRequired;
+
+ /// an XPath-expression to determine if item is valid
+ BoolExpression maConstraint;
+
+ /// user-readable explanation of the constraint
+ OUString msExplainConstraint;
+
+ /// an XPath-expression to calculate values
+ ComputedExpression maCalculate;
+
+ /// the XML namespaces used for XML names/XPath-expressions in this binding
+ css::uno::Reference<css::container::XNameContainer> mxNamespaces;
+
+ /// a type name
+ OUString msTypeName;
+
+ /// modify listeners
+ ModifyListeners_t maModifyListeners;
+
+ /// list entry listener
+ XListEntryListeners_t maListEntryListeners;
+
+ /// validity listeners;
+ XValidityConstraintListeners_t maValidityListeners;
+
+ /// nodes on which we are listening for events
+ std::vector<css::uno::Reference<css::xml::dom::XNode> > maEventNodes;
+
+ /// the current MIP object for the first node we are bound to
+ MIP maMIP;
+
+ /// flag to detect recursions in calculate
+ bool mbInCalculate;
+
+ // flags to manage deferred notifications:
+ /// if >0, valueModified() and bindingModified() will only set flags
+ sal_Int32 mnDeferModifyNotifications;
+ bool mbValueModified; /// if true, valueModified needs to be called
+ bool mbBindingModified; /// if true, bindingModified needs to be called
+
+
+ void initializePropertySet();
+
+
+public:
+ Binding();
+ virtual ~Binding() override;
+
+
+ // property methods: get/set value
+
+
+ css::uno::Reference<css::xforms::XModel> getModel() const { return mxModel;} /// get XForms model
+ void _setModel( const css::uno::Reference<css::xforms::XModel>& ); /// set XForms model (only called by Model)
+
+
+ OUString getModelID() const; /// get ID of XForms model
+
+ OUString getBindingID() const { return msBindingID;} /// get ID for this binding
+ void setBindingID( const OUString& ); /// set ID for this binding
+
+ OUString getBindingExpression() const; /// get binding expression
+ void setBindingExpression( const OUString& ); /// set binding exp.
+
+ // MIPs (model item properties)
+
+ OUString getReadonlyExpression() const; /// get read-only MIP
+ void setReadonlyExpression( const OUString& ); /// set read-only MIP
+
+ OUString getRelevantExpression() const; /// get relevant MIP
+ void setRelevantExpression( const OUString& ); /// set relevant MIP
+
+ OUString getRequiredExpression() const; /// get required MIP
+ void setRequiredExpression( const OUString& ); /// set required MIP
+
+ OUString getConstraintExpression() const; /// get constraint MIP
+ void setConstraintExpression( const OUString& );/// set constraint MIP
+
+ OUString getCalculateExpression() const; /// get calculate MIP
+ void setCalculateExpression( const OUString& ); /// set calculate MIP
+
+ OUString getType() const { return msTypeName;} /// get type name MIP (static)
+ void setType( const OUString& ); /// set type name MIP (static)
+
+ // a binding expression can only be interpreted with respect to
+ // suitable namespace declarations. We collect those in the model and in a binding.
+
+ // access to a binding's namespace
+ // (set-method only changes local namespaces (but may add to model))
+ css::uno::Reference<css::container::XNameContainer> getBindingNamespaces() const { return mxNamespaces; }
+ void setBindingNamespaces( const css::uno::Reference<css::container::XNameContainer>& ); /// get binding nmsp.
+
+ // access to the model's namespaces
+ // (set-method changes model's namespaces (unless a local one is present))
+ css::uno::Reference<css::container::XNameContainer> getModelNamespaces() const; /// set model namespaces
+ void setModelNamespaces( const css::uno::Reference<css::container::XNameContainer>& ); /// get model nmsp.
+
+
+ // read-only properties that map MIPs to control data source properties
+ bool getReadOnly() const; // MIP readonly
+ bool getRelevant() const; // MIP relevant
+ bool getExternalData() const; // mapped from model's ExternalData property
+
+
+ // missing binding properties:
+ // - type (static; default: xsd:string)
+ // - minOccurs/maxOccurs (computed XPath; default: 0/inf)
+ // - p3ptype (static; no default)
+
+
+ /// get this binding's context node
+ xforms::EvaluationContext getEvaluationContext() const;
+
+ /// get evaluation contexts for this binding's MIPs
+ std::vector<xforms::EvaluationContext> getMIPEvaluationContexts();
+
+ /// get nodeset the bind is bound to
+ css::uno::Reference<css::xml::dom::XNodeList> getXNodeList();
+
+ /// heuristically determine whether this binding is simple binding
+ /// (here: simple binding == does not depend on other parts of the
+ /// instance, it's not a 'dynamic' binding)
+ bool isSimpleBinding() const;
+
+ /// heuristically determine whether this binding's binding
+ /// expression is simple
+ bool isSimpleBindingExpression() const;
+
+ /// update this binding (e.g. called by model for refresh )
+ void update();
+
+ /// prevent change notifications being sent to controls
+ void deferNotifications( bool );
+
+ /// is this binding valid? (are constraint, type and required MIPs ok?)
+ bool isValid() const;
+
+ /// determine whether this binding currently performs a useful
+ /// function, r whether is may be discarded
+ bool isUseful() const;
+
+ /// explain why binding is invalid
+ OUString explainInvalid();
+
+
+ // the ID for XUnoTunnel calls
+ static css::uno::Sequence<sal_Int8> getUnoTunnelId();
+
+
+private:
+ /// check whether object is live, and throw suitable exception if not
+ /// (to be used be API methods before acting on the object)
+ ///
+ /// @throws css::uno::RuntimeException
+ void checkLive();
+
+ /// determine whether object is live
+ /// live: has model, and model has been initialized
+ bool isLive() const;
+
+ /// get the model implementation
+ xforms::Model* getModelImpl() const;
+
+ /// get MIP evaluation contexts
+ /// (only valid if control has already been bound)
+ std::vector<xforms::EvaluationContext> _getMIPEvaluationContexts() const;
+
+ /// bind this binding, and pre-compute the affected nodes
+ void bind( bool bForceRebind = false );
+
+ /// the binding value has been changed:
+ /// trigger a modified event on all modified listeners
+ void valueModified();
+
+ /// the binding itself has changed:
+ /// force rebind, then call valueModified()
+ void bindingModified();
+
+
+ /// set MIPs defined by this binding on MIP item
+ MIP getLocalMIP() const;
+
+ /// get the data type that applies to this binding
+ css::uno::Reference<css::xsd::XDataType> getDataType() const;
+
+ /// determine whether binding is valid according to the given data type
+ bool isValid_DataType() const;
+
+ /// explain validity of binding with respect to the given data type
+ OUString explainInvalid_DataType();
+
+ /// 'clear' this binding - remove all listeners, etc.
+ void clear();
+
+ /// distribute MIPs from current node recursively to children
+ void distributeMIP( const css::uno::Reference<css::xml::dom::XNode> &rxNode );
+
+ /// implement get*Namespaces()
+ css::uno::Reference<css::container::XNameContainer> _getNamespaces() const;
+
+ /// implement set*Namespaces()
+ void _setNamespaces( const css::uno::Reference<css::container::XNameContainer>&, bool bBinding );
+
+ /// set a useful default binding ID (if none is set)
+ void _checkBindingID();
+
+public:
+
+ virtual css::uno::Sequence<css::uno::Type> SAL_CALL getSupportedValueTypes() override;
+
+ virtual sal_Bool SAL_CALL supportsType( const css::uno::Type& aType ) override;
+
+ virtual css::uno::Any SAL_CALL getValue( const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL setValue( const css::uno::Any& aValue ) override;
+
+
+ // XListEntrySource
+
+
+ virtual sal_Int32 SAL_CALL getListEntryCount() override;
+
+ virtual OUString SAL_CALL getListEntry( sal_Int32 nPosition ) override;
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getAllListEntries() override;
+
+ virtual void SAL_CALL addListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>& ) override;
+
+ virtual void SAL_CALL removeListEntryListener( const css::uno::Reference<css::form::binding::XListEntryListener>&) override;
+
+
+ // XValidator:
+
+
+ virtual sal_Bool SAL_CALL isValid(
+ const css::uno::Any& ) override;
+
+ virtual OUString SAL_CALL explainInvalid(
+ const css::uno::Any& ) override;
+
+ virtual void SAL_CALL addValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener ) override;
+
+ virtual void SAL_CALL removeValidityConstraintListener(
+ const css::uno::Reference<css::form::validation::XValidityConstraintListener>& xListener ) override;
+
+
+ // XModifyBroadcaster & friends:
+ // inform listeners about changes in our values
+
+
+public:
+
+ virtual void SAL_CALL addModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener ) override;
+
+ virtual void SAL_CALL removeModifyListener(
+ const css::uno::Reference<css::util::XModifyListener>& xListener ) override;
+
+
+ // XNamed:
+ // get/set name
+
+
+public:
+
+ virtual OUString SAL_CALL getName() override;
+
+ virtual void SAL_CALL setName( const OUString& ) override;
+
+
+ // xml::dom::event::XEventListener
+ // receive an event if our node changed
+
+
+ virtual void SAL_CALL handleEvent(
+ const css::uno::Reference<css::xml::dom::events::XEvent>& xEvent ) override;
+
+
+ // XUnoTunnel
+
+
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence<sal_Int8>& ) override;
+
+
+ // XCloneable
+
+
+ virtual css::uno::Reference<css::util::XCloneable> SAL_CALL createClone() override;
+};
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/boolexpression.cxx b/forms/source/xforms/boolexpression.cxx
new file mode 100644
index 000000000..af45c8c38
--- /dev/null
+++ b/forms/source/xforms/boolexpression.cxx
@@ -0,0 +1,42 @@
+/* -*- 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 "boolexpression.hxx"
+
+namespace xforms
+{
+/** BoolExpression represents a computed XPath expression that returns
+ * a bool value and caches the results.
+ *
+ * As this class has no virtual methods, it should never be used
+ * polymorphically. */
+
+BoolExpression::BoolExpression() {}
+
+BoolExpression::~BoolExpression() {}
+
+void BoolExpression::setExpression(const OUString& rExpression)
+{
+ ComputedExpression::setExpression(rExpression);
+ mbIsSimple = _checkExpression(" *(true)|(false) *\\( *\\) *");
+}
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/boolexpression.hxx b/forms/source/xforms/boolexpression.hxx
new file mode 100644
index 000000000..ddbc13ce7
--- /dev/null
+++ b/forms/source/xforms/boolexpression.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 "computedexpression.hxx"
+
+namespace xforms
+{
+/** BoolExpression represents a computed XPath expression that returns
+ * a bool value and caches the results.
+ *
+ * As this class has no virtual methods, it should never be used
+ * polymorphically. */
+class BoolExpression : public ComputedExpression
+{
+public:
+ BoolExpression();
+ ~BoolExpression();
+
+ /// set the expression string
+ /// (overridden for new definition of a simple expression)
+ void setExpression(const OUString& rExpression);
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/collection.hxx b/forms/source/xforms/collection.hxx
new file mode 100644
index 000000000..00c7bdc39
--- /dev/null
+++ b/forms/source/xforms/collection.hxx
@@ -0,0 +1,280 @@
+/* -*- 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 "enumeration.hxx"
+
+#include <cppuhelper/implbase.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <vector>
+
+
+typedef cppu::WeakImplHelper<
+ css::container::XIndexReplace,
+ css::container::XSet,
+ css::container::XContainer>
+Collection_t;
+
+template<class ELEMENT_TYPE>
+class Collection : public Collection_t
+{
+public:
+ typedef ELEMENT_TYPE T;
+ typedef std::vector<css::uno::Reference<css::container::XContainerListener> > Listeners_t;
+
+protected:
+ std::vector<T> maItems;
+ Listeners_t maListeners;
+
+public:
+
+ Collection() {}
+
+ const T& getItem( sal_Int32 n ) const
+ {
+ OSL_ENSURE( isValidIndex(n), "invalid index" );
+ OSL_ENSURE( isValid( maItems[n] ), "invalid item found" );
+ return maItems[n];
+ }
+
+ void setItem( sal_Int32 n, const T& t)
+ {
+ OSL_ENSURE( isValidIndex(n), "invalid index" );
+ OSL_ENSURE( isValid ( t ), "invalid item" );
+
+ T& aRef = maItems[ n ];
+ _elementReplaced( n, t );
+ _remove( aRef );
+ aRef = t;
+ _insert( t );
+ }
+
+ bool hasItem( const T& t ) const
+ {
+ return maItems.end() != std::find( maItems.begin(), maItems.end(), t );
+ }
+
+ sal_Int32 addItem( const T& t )
+ {
+ OSL_ENSURE( !hasItem( t ), "item to be added already present" );
+ OSL_ENSURE( isValid( t ), "invalid item" );
+
+ maItems.push_back( t );
+ _insert( t );
+ _elementInserted( maItems.size() - 1 );
+ return ( maItems.size() - 1 );
+ }
+
+ void removeItem( const T& t )
+ {
+ OSL_ENSURE( hasItem( t ), "item to be removed not present" );
+ OSL_ENSURE( isValid( t ), "an invalid item, funny that!" );
+
+ _elementRemoved( t );
+ _remove( t );
+ maItems.erase( std::find( maItems.begin(), maItems.end(), t ) );
+ }
+
+ bool hasItems() const
+ {
+ return maItems.size() != 0;
+ }
+
+ sal_Int32 countItems() const
+ {
+ return static_cast<sal_Int32>( maItems.size() );
+ }
+
+ bool isValidIndex( sal_Int32 n ) const
+ {
+ return n >= 0 && n < static_cast<sal_Int32>( maItems.size() );
+ }
+
+
+ // the following method may be overridden by sub-classes for
+ // customized behaviour
+
+ /// called before insertion to determine whether item is valid
+ virtual bool isValid( const T& ) const { return true; }
+
+
+protected:
+
+ // the following methods may be overridden by sub-classes for
+ // customized behaviour
+
+ /// called after item has been inserted into the collection
+ virtual void _insert( const T& ) { }
+
+ /// called before item is removed from the collection
+ virtual void _remove( const T& ) { }
+
+public:
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<T>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return hasItems();
+ }
+
+ // XIndexAccess : XElementAccess
+ virtual sal_Int32 SAL_CALL getCount() override
+ {
+ return countItems();
+ }
+
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override
+ {
+ if( !isValidIndex( nIndex ) )
+ throw css::lang::IndexOutOfBoundsException();
+ return css::uno::Any( getItem( nIndex ) );
+ }
+
+ // XIndexReplace : XIndexAccess
+ virtual void SAL_CALL replaceByIndex( sal_Int32 nIndex,
+ const css::uno::Any& aElement ) override
+ {
+ T t;
+ if( !isValidIndex( nIndex) )
+ throw css::lang::IndexOutOfBoundsException();
+ if( !( aElement >>= t ) || !isValid( t ) )
+ throw css::lang::IllegalArgumentException();
+ setItem( nIndex, t );
+ }
+
+ // XEnumerationAccess : XElementAccess
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override
+ {
+ return new Enumeration( this );
+ }
+
+
+ // XSet : XEnumerationAccess
+ virtual sal_Bool SAL_CALL has( const css::uno::Any& aElement ) override
+ {
+ T t;
+ return ( aElement >>= t ) && hasItem( t );
+ }
+
+ virtual void SAL_CALL insert( const css::uno::Any& aElement ) override
+ {
+ T t;
+ if( !( aElement >>= t ) || !isValid( t ) )
+ throw css::lang::IllegalArgumentException();
+ if( hasItem( t ) )
+ throw css::container::ElementExistException();
+ addItem( t );
+ }
+
+ virtual void SAL_CALL remove( const css::uno::Any& aElement ) override
+ {
+ T t;
+ if( !(aElement >>= t) )
+ throw css::lang::IllegalArgumentException();
+ if( !hasItem( t ) )
+ throw css::container::NoSuchElementException();
+ removeItem( t );
+ }
+
+
+ // XContainer
+ virtual void SAL_CALL addContainerListener(
+ const css::uno::Reference<css::container::XContainerListener>& xListener ) override
+ {
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ if( std::find( maListeners.begin(), maListeners.end(), xListener)
+ == maListeners.end() )
+ maListeners.push_back( xListener );
+ }
+
+ virtual void SAL_CALL removeContainerListener(
+ const css::uno::Reference<css::container::XContainerListener>& xListener ) override
+ {
+ OSL_ENSURE( xListener.is(), "need listener!" );
+ Listeners_t::iterator aIter =
+ std::find( maListeners.begin(), maListeners.end(), xListener );
+ if( aIter != maListeners.end() )
+ maListeners.erase( aIter );
+ }
+
+protected:
+
+ // call listeners:
+ void _elementInserted( sal_Int32 nPos )
+ {
+ OSL_ENSURE( isValidIndex(nPos), "invalid index" );
+ css::container::ContainerEvent aEvent(
+ static_cast<css::container::XIndexReplace*>( this ),
+ css::uno::Any( nPos ),
+ css::uno::Any( getItem( nPos ) ),
+ css::uno::Any() );
+ for (auto const& listener : maListeners)
+ {
+ listener->elementInserted( aEvent );
+ }
+ }
+
+ void _elementRemoved( const T& aOld )
+ {
+ css::container::ContainerEvent aEvent(
+ static_cast<css::container::XIndexReplace*>( this ),
+ css::uno::Any(),
+ css::uno::Any( aOld ),
+ css::uno::Any() );
+ for (auto const& listener : maListeners)
+ {
+ listener->elementRemoved( aEvent );
+ }
+ }
+
+ void _elementReplaced( const sal_Int32 nPos, const T& aNew )
+ {
+ OSL_ENSURE( isValidIndex(nPos), "invalid index" );
+ css::container::ContainerEvent aEvent(
+ static_cast<css::container::XIndexReplace*>( this ),
+ css::uno::Any( nPos ),
+ css::uno::Any( getItem( nPos ) ),
+ css::uno::Any( aNew ) );
+ for (auto const& listener : maListeners)
+ {
+ listener->elementReplaced( aEvent );
+ }
+ }
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/computedexpression.cxx b/forms/source/xforms/computedexpression.cxx
new file mode 100644
index 000000000..add8d4350
--- /dev/null
+++ b/forms/source/xforms/computedexpression.cxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "computedexpression.hxx"
+#include "evaluationcontext.hxx"
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathExtension.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+
+#include <osl/diagnose.h>
+
+#include <i18nutil/searchopt.hxx>
+#include <unotools/textsearch.hxx>
+#include <comphelper/processfactory.hxx>
+
+using namespace com::sun::star::uno;
+using com::sun::star::xml::xpath::XPathAPI;
+using com::sun::star::xml::xpath::XXPathAPI;
+using com::sun::star::xml::xpath::XPathExtension;
+using com::sun::star::xml::xpath::XXPathExtension;
+using com::sun::star::xml::xpath::XPathObjectType_XPATH_UNDEFINED;
+
+
+namespace xforms
+{
+
+ComputedExpression::ComputedExpression()
+ : mbIsEmpty( true ),
+ mbIsSimple( true )
+{
+}
+
+ComputedExpression::~ComputedExpression()
+{
+}
+
+
+void ComputedExpression::setExpression( const OUString& rExpression )
+{
+ // set new expression, and clear pre-computed results
+ msExpression = rExpression;
+ mbIsEmpty = _checkExpression( " *" );
+ mbIsSimple = false;
+ mxResult.clear();
+}
+
+
+bool ComputedExpression::_checkExpression( const char* pExpression ) const
+{
+ assert(pExpression && "no expression?");
+
+ // call RegExp engine
+ i18nutil::SearchOptions2 aSearchOptions;
+ aSearchOptions.AlgorithmType2 = css::util::SearchAlgorithms2::REGEXP;
+ aSearchOptions.searchString = OUString( pExpression, strlen(pExpression), RTL_TEXTENCODING_ASCII_US );
+ utl::TextSearch aTextSearch( aSearchOptions );
+
+ sal_Int32 nLength = msExpression.getLength();
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = nLength;
+ bool bSearch = aTextSearch.SearchForward( msExpression, &nStart, &nEnd );
+
+ // our expression is static only if 1) we found our regexp, and 2)
+ // the regexp goes from beginning to end.
+ return ( nLength == 0 || bSearch )
+ && ( nStart == 0 && nEnd == nLength );
+}
+
+bool ComputedExpression::isSimpleExpression() const
+{
+ // actual work is done by setExpression
+ return mbIsEmpty || mbIsSimple;
+}
+
+
+bool ComputedExpression::_evaluate(
+ const xforms::EvaluationContext& rContext,
+ const OUString& sExpression )
+{
+ OSL_ENSURE( rContext.mxContextNode.is(), "no context node in context" );
+
+ // obtain value by evaluating XPath expression
+ mxResult.clear();
+ try
+ {
+ mxResult = _getXPathAPI(rContext)->eval( rContext.mxContextNode,
+ sExpression );
+ }
+ catch( const Exception& )
+ {
+ ; // ignore exception -> mxResult will be empty
+ }
+
+ return hasValue();
+}
+
+bool ComputedExpression::evaluate( const EvaluationContext& rContext )
+{
+ // for simple expression we don't need to re-evaluate (if we have
+ // an older result); neither for empty expressions
+ if( mbIsEmpty || (mxResult.is() && mbIsSimple) )
+ return true;
+
+ return _evaluate( rContext, _getExpressionForEvaluation() );
+}
+
+
+bool ComputedExpression::hasValue() const
+{
+ return mxResult.is() &&
+ mxResult->getObjectType() != XPathObjectType_XPATH_UNDEFINED;
+}
+
+void ComputedExpression::clear()
+{
+ mxResult.clear();
+}
+
+
+OUString ComputedExpression::getString() const
+{
+ return mxResult.is() ? mxResult->getString() : OUString();
+}
+
+bool ComputedExpression::getBool( bool bDefault ) const
+{
+ return mxResult.is() ? mxResult->getBoolean() : bDefault;
+}
+
+
+Reference<XXPathAPI> ComputedExpression::_getXPathAPI(const xforms::EvaluationContext& aContext)
+{
+ // create XPath API, then register namespaces
+ Reference<XXPathAPI> xXPath( XPathAPI::create( comphelper::getProcessComponentContext() ) );
+
+ // register xforms extension#
+ Reference< XComponentContext > aComponentContext = comphelper::getProcessComponentContext();
+ Reference< XXPathExtension > aExtension = XPathExtension::createWithModel(aComponentContext, aContext.mxModel, aContext.mxContextNode);
+ xXPath->registerExtensionInstance(aExtension);
+
+ // register namespaces
+ if( aContext.mxNamespaces.is() )
+ {
+ Sequence<OUString> aPrefixes =aContext.mxNamespaces->getElementNames();
+ sal_Int32 nCount = aPrefixes.getLength();
+ const OUString* pPrefixes = aPrefixes.getConstArray();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ const OUString* pNamePrefix = &pPrefixes[i];
+ OUString sNameURL;
+ aContext.mxNamespaces->getByName( *pNamePrefix ) >>= sNameURL;
+ xXPath->registerNS( *pNamePrefix, sNameURL );
+ }
+ }
+
+ // done, so return xXPath-object
+ return xXPath;
+}
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/computedexpression.hxx b/forms/source/xforms/computedexpression.hxx
new file mode 100644
index 000000000..17f39cd21
--- /dev/null
+++ b/forms/source/xforms/computedexpression.hxx
@@ -0,0 +1,121 @@
+/* -*- 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/uno/Reference.hxx>
+
+// forward declaractions
+namespace com::sun::star
+{
+ namespace xml
+ {
+ namespace dom { class XNode; }
+ namespace dom { class XNodeset; }
+ namespace xpath { class XXPathAPI; }
+ namespace xpath { class XXPathObject; }
+ }
+ namespace container { class XNameContainer; }
+}
+namespace xforms { class EvaluationContext; }
+
+
+namespace xforms
+{
+
+/** ComputedExpression represents an XPath Expression and caches results.
+ *
+ * As this class has no virtual methods, it should never be used
+ * polymorphically. */
+class ComputedExpression
+{
+ /// the expression string
+ OUString msExpression;
+
+ /// is msExpression empty?
+ bool mbIsEmpty;
+
+protected:
+ /// is msExpression a simple expression?
+ bool mbIsSimple;
+
+ /// the result from the last bind
+ css::uno::Reference<css::xml::xpath::XXPathObject> mxResult;
+
+
+ /// implementation of isSimpleExpression
+ bool _checkExpression( const char* pExpression ) const;
+
+ /// allow manipulation of the expression before it is evaluated
+ // the default implementation is to do nothing...
+ const OUString& _getExpressionForEvaluation() const { return msExpression; }
+
+ /// obtain a (suitable) XPathAPI implementation
+ static css::uno::Reference<css::xml::xpath::XXPathAPI> _getXPathAPI(const xforms::EvaluationContext& aContext);
+
+ /// evaluate the expression relative to the content node.
+ bool _evaluate( const xforms::EvaluationContext& rContext,
+ const OUString& sExpression );
+
+
+public:
+ ComputedExpression();
+ ~ComputedExpression();
+
+
+ /// get the expression string
+ const OUString& getExpression() const { return msExpression;}
+
+ /// set a new expression string
+ void setExpression( const OUString& rExpression );
+
+ /// do we have an actual expression?
+ bool isEmptyExpression() const { return mbIsEmpty;}
+
+ /// heuristically determine whether this expression is 'simple',
+ /// i.e. whether its value will change depending on the values
+ /// of other nodes
+ bool isSimpleExpression() const;
+
+
+ /// evaluate the expression relative to the content node.
+ bool evaluate( const xforms::EvaluationContext& rContext );
+
+
+ /// does this expression have a value?
+ bool hasValue() const;
+
+
+ /// remove value/evaluate results
+ void clear();
+
+
+ // get the result of this expression as string/bool/...
+ // (Results will be based on the last call of evaluate(..). The caller
+ // must call evaluate to ensure current results.)
+ css::uno::Reference<css::xml::xpath::XXPathObject> const & getXPath() const { return mxResult;}
+ bool getBool( bool bDefault = false ) const;
+ OUString getString() const;
+
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/convert.cxx b/forms/source/xforms/convert.cxx
new file mode 100644
index 000000000..28d30885f
--- /dev/null
+++ b/forms/source/xforms/convert.cxx
@@ -0,0 +1,335 @@
+/* -*- 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 "convert.hxx"
+
+#include <sstream>
+#include <rtl/math.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <tools/date.hxx>
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <comphelper/sequence.hxx>
+#include <unotools/datetime.hxx>
+
+using xforms::Convert;
+using com::sun::star::uno::Any;
+using namespace utl;
+
+Convert::Convert()
+{
+ init();
+}
+
+namespace
+{
+
+ OUString lcl_toXSD_OUString( const Any& rAny )
+ { OUString sStr; rAny >>= sStr; return sStr; }
+
+
+ Any lcl_toAny_OUString( const OUString& rStr )
+ { return Any(rStr); }
+
+ OUString lcl_toXSD_bool( const Any& rAny )
+ { bool b = false; rAny >>= b; return b ? OUString("true") : OUString("false"); }
+
+ Any lcl_toAny_bool( const OUString& rStr )
+ {
+ bool b = ( rStr == "true" || rStr == "1" );
+ return Any( b );
+ }
+
+ OUString lcl_toXSD_double( const Any& rAny )
+ {
+ double f = 0.0;
+ rAny >>= f;
+
+ return std::isfinite( f )
+ ? rtl::math::doubleToUString( f, rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.',
+ true )
+ : OUString();
+ }
+
+
+ Any lcl_toAny_double( const OUString& rString )
+ {
+ rtl_math_ConversionStatus eStatus;
+ double f = rtl::math::stringToDouble(
+ rString, '.', ',', &eStatus );
+ return ( eStatus == rtl_math_ConversionStatus_Ok ) ? Any( f ) : Any();
+ }
+
+ void lcl_appendInt32ToBuffer( const sal_Int32 _nValue, OUStringBuffer& _rBuffer, sal_Int16 _nMinDigits )
+ {
+ if ( ( _nMinDigits >= 4 ) && ( _nValue < 1000 ) )
+ _rBuffer.append( '0' );
+ if ( ( _nMinDigits >= 3 ) && ( _nValue < 100 ) )
+ _rBuffer.append( '0' );
+ if ( ( _nMinDigits >= 2 ) && ( _nValue < 10 ) )
+ _rBuffer.append( '0' );
+ _rBuffer.append( _nValue );
+ }
+
+
+ OUString lcl_toXSD_UNODate_typed( const css::util::Date& rDate )
+ {
+
+ OUStringBuffer sInfo;
+ lcl_appendInt32ToBuffer( rDate.Year, sInfo, 4 );
+ sInfo.append( "-" );
+ lcl_appendInt32ToBuffer( rDate.Month, sInfo, 2 );
+ sInfo.append( "-" );
+ lcl_appendInt32ToBuffer( rDate.Day, sInfo, 2 );
+
+ return sInfo.makeStringAndClear();
+ }
+
+
+ OUString lcl_toXSD_UNODate( const Any& rAny )
+ {
+ css::util::Date aDate;
+ OSL_VERIFY( rAny >>= aDate );
+ return lcl_toXSD_UNODate_typed( aDate );
+ }
+
+
+ css::util::Date lcl_toUNODate( std::u16string_view rString )
+ {
+ css::util::Date aDate( 1, 1, 1900 );
+
+ bool bWellformed = ISO8601parseDate(rString, aDate);
+
+ // sanity checks
+ if ( ( aDate.Year > 9999 ) || ( aDate.Month < 1 ) || ( aDate.Month > 12 ) || ( aDate.Day < 1 ) || ( aDate.Day > 31 ) )
+ bWellformed = false;
+ else
+ {
+ ::Date aDateCheck( 1, aDate.Month, aDate.Year );
+ if ( aDate.Day > aDateCheck.GetDaysInMonth() )
+ bWellformed = false;
+ }
+
+ // all okay?
+ if ( !bWellformed )
+ return css::util::Date( 1, 1, 1900 );
+
+ return aDate;
+ }
+
+
+ Any lcl_toAny_UNODate( const OUString& rString )
+ {
+ return Any( lcl_toUNODate( rString ) );
+ }
+
+
+ OUString lcl_toXSD_UNOTime_typed( const css::util::Time& rTime )
+ {
+
+ OUStringBuffer sInfo;
+ lcl_appendInt32ToBuffer( rTime.Hours, sInfo, 2 );
+ sInfo.append( ":" );
+ lcl_appendInt32ToBuffer( rTime.Minutes, sInfo, 2 );
+ sInfo.append( ":" );
+ lcl_appendInt32ToBuffer( rTime.Seconds, sInfo, 2 );
+ if ( rTime.NanoSeconds != 0 )
+ {
+ OSL_ENSURE(rTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
+ sInfo.append('.');
+ std::ostringstream ostr;
+ ostr.fill('0');
+ ostr.width(9);
+ ostr << rTime.NanoSeconds;
+ sInfo.append(OUString::createFromAscii(ostr.str().c_str()));
+ }
+
+ return sInfo.makeStringAndClear();
+ }
+
+
+ OUString lcl_toXSD_UNOTime( const Any& rAny )
+ {
+ css::util::Time aTime;
+ OSL_VERIFY( rAny >>= aTime );
+ return lcl_toXSD_UNOTime_typed( aTime );
+ }
+
+
+ css::util::Time lcl_toUNOTime( std::u16string_view rString )
+ {
+ css::util::Time aTime;
+
+ bool bWellformed = ISO8601parseTime(rString, aTime);
+
+ // sanity checks
+ // note that Seconds == 60 denotes leap seconds. Normally, they're not allowed everywhere,
+ // but we accept them all the time for simplicity reasons
+ if ( ( aTime.Hours > 24 )
+ || ( aTime.Minutes > 59 )
+ || ( aTime.Seconds > 60 )
+ )
+ bWellformed = false;
+
+ if ( bWellformed
+ && ( aTime.Hours == 24 )
+ && ( ( aTime.Minutes != 0 )
+ || ( aTime.Seconds != 0 )
+ || ( aTime.NanoSeconds != 0 )
+ )
+ )
+ bWellformed = false;
+
+ // all okay?
+ if ( !bWellformed )
+ return css::util::Time();
+
+ return aTime;
+ }
+
+
+ Any lcl_toAny_UNOTime( const OUString& rString )
+ {
+ return Any( lcl_toUNOTime( rString ) );
+ }
+
+
+ OUString lcl_toXSD_UNODateTime( const Any& rAny )
+ {
+ css::util::DateTime aDateTime;
+ OSL_VERIFY( rAny >>= aDateTime );
+
+ css::util::Date aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year );
+ OUString sDate = lcl_toXSD_UNODate_typed( aDate );
+
+ css::util::Time const aTime( aDateTime.NanoSeconds, aDateTime.Seconds,
+ aDateTime.Minutes, aDateTime.Hours, aDateTime.IsUTC);
+ OUString sTime = lcl_toXSD_UNOTime_typed( aTime );
+
+ OUString sRet = sDate + "T" + sTime;
+ return sRet;
+ }
+
+
+ Any lcl_toAny_UNODateTime( const OUString& rString )
+ {
+ // separate the date from the time part
+ sal_Int32 nDateTimeSep = rString.indexOf( 'T' );
+ if ( nDateTimeSep == -1 )
+ nDateTimeSep = rString.indexOf( 't' );
+
+ css::util::Date aDate;
+ css::util::Time aTime;
+ if ( nDateTimeSep == -1 )
+ { // no time part
+ aDate = lcl_toUNODate( rString );
+ }
+ else
+ {
+ aDate = lcl_toUNODate( rString.subView( 0, nDateTimeSep ) );
+ aTime = lcl_toUNOTime( rString.subView( nDateTimeSep + 1 ) );
+ }
+ css::util::DateTime aDateTime(
+ aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
+ aDate.Day, aDate.Month, aDate.Year, aTime.IsUTC
+ );
+ return Any( aDateTime );
+ }
+}
+
+
+void Convert::init()
+{
+ maMap[ cppu::UnoType<OUString>::get() ] = Convert_t(&lcl_toXSD_OUString, &lcl_toAny_OUString);
+ maMap[ cppu::UnoType<bool>::get() ] = Convert_t(&lcl_toXSD_bool, &lcl_toAny_bool);
+ maMap[ cppu::UnoType<double>::get() ] = Convert_t(&lcl_toXSD_double, &lcl_toAny_double);
+ maMap[ cppu::UnoType<css::util::Date>::get() ] = Convert_t( &lcl_toXSD_UNODate, &lcl_toAny_UNODate );
+ maMap[ cppu::UnoType<css::util::Time>::get() ] = Convert_t( &lcl_toXSD_UNOTime, &lcl_toAny_UNOTime );
+ maMap[ cppu::UnoType<css::util::DateTime>::get() ] = Convert_t( &lcl_toXSD_UNODateTime, &lcl_toAny_UNODateTime );
+}
+
+
+Convert& Convert::get()
+{
+ // create our Singleton instance on demand
+ static Convert aConvert;
+ return aConvert;
+}
+
+bool Convert::hasType( const css::uno::Type& rType )
+{
+ return maMap.find( rType ) != maMap.end();
+}
+
+css::uno::Sequence<css::uno::Type> Convert::getTypes() const
+{
+ return comphelper::mapKeysToSequence( maMap );
+}
+
+OUString Convert::toXSD( const css::uno::Any& rAny )
+{
+ Map_t::iterator aIter = maMap.find( rAny.getValueType() );
+ return aIter != maMap.end() ? aIter->second.first( rAny ) : OUString();
+}
+
+css::uno::Any Convert::toAny( const OUString& rValue,
+ const css::uno::Type& rType )
+{
+ Map_t::iterator aIter = maMap.find( rType );
+ return aIter != maMap.end() ? aIter->second.second( rValue ) : css::uno::Any();
+}
+
+
+OUString Convert::collapseWhitespace( const OUString& _rString )
+{
+ sal_Int32 nLength = _rString.getLength();
+ OUStringBuffer aBuffer( nLength );
+ const sal_Unicode* pStr = _rString.getStr();
+ bool bStrip = true;
+ for( sal_Int32 i = 0; i < nLength; i++ )
+ {
+ sal_Unicode c = pStr[i];
+ if( c == u'\x0008' ||
+ c == u'\x000A' ||
+ c == u'\x000D' ||
+ c == u' ' )
+ {
+ if( ! bStrip )
+ {
+ aBuffer.append( u' ' );
+ bStrip = true;
+ }
+ }
+ else
+ {
+ bStrip = false;
+ aBuffer.append( c );
+ }
+ }
+ if( aBuffer[ aBuffer.getLength() - 1 ] == u' ' )
+ aBuffer.setLength( aBuffer.getLength() - 1 );
+ return aBuffer.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/convert.hxx b/forms/source/xforms/convert.hxx
new file mode 100644
index 000000000..c7073f7c3
--- /dev/null
+++ b/forms/source/xforms/convert.hxx
@@ -0,0 +1,78 @@
+/* -*- 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/uno/Sequence.hxx>
+#include <map>
+
+namespace com::sun::star::uno
+{
+ class Any;
+ class Type;
+}
+
+namespace xforms
+{
+
+struct TypeLess
+{
+ bool operator()( const css::uno::Type& rType1,
+ const css::uno::Type& rType2 ) const
+ { return rType1.getTypeName() < rType2.getTypeName(); }
+};
+
+class Convert
+{
+ // hold conversion objects
+ typedef OUString (*fn_toXSD)( const css::uno::Any& );
+ typedef css::uno::Any (*fn_toAny)( const OUString& );
+ typedef std::pair<fn_toXSD,fn_toAny> Convert_t;
+ typedef std::map<css::uno::Type, Convert_t, TypeLess> Map_t;
+ Map_t maMap;
+
+ Convert();
+
+ void init();
+
+public:
+ /** get/create Singleton class */
+ static Convert& get();
+
+ /// can we convert this type?
+ bool hasType( const css::uno::Type& );
+
+ /// get list of convertible types
+ css::uno::Sequence<css::uno::Type> getTypes() const;
+
+ /// convert any to XML representation
+ OUString toXSD( const css::uno::Any& rAny );
+
+ /// convert XML representation to Any of given type
+ css::uno::Any toAny( const OUString&, const css::uno::Type& );
+
+ /** replace all sequences of 0x08, 0x0A, 0x0D, 0x20 with a single 0x20.
+ also strip leading/trailing whitespace.
+ */
+ static OUString collapseWhitespace( const OUString& _rString );
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatyperepository.cxx b/forms/source/xforms/datatyperepository.cxx
new file mode 100644
index 000000000..fc446a5a3
--- /dev/null
+++ b/forms/source/xforms/datatyperepository.cxx
@@ -0,0 +1,295 @@
+/* -*- 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 "datatyperepository.hxx"
+#include "datatypes.hxx"
+#include <strings.hrc>
+#include <frm_resource.hxx>
+#include <frm_strings.hxx>
+#include <property.hxx>
+
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <comphelper/enumhelper.hxx>
+
+namespace xforms
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::util::VetoException;
+ using ::com::sun::star::container::NoSuchElementException;
+ using ::com::sun::star::container::ElementExistException;
+ using ::com::sun::star::container::XEnumeration;
+ using ::com::sun::star::xsd::XDataType;
+ using namespace frm;
+
+ ODataTypeRepository::ODataTypeRepository( )
+ {
+
+ // insert some basic types
+ OUString sName( ResourceManager::loadString(RID_STR_DATATYPE_STRING) );
+ m_aRepository[ sName ] = new OStringType( sName, css::xsd::DataTypeClass::STRING );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_URL);
+ m_aRepository[ sName ] = new OStringType( sName, css::xsd::DataTypeClass::anyURI );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_BOOLEAN);
+ m_aRepository[ sName ] = new OBooleanType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DECIMAL);
+ m_aRepository[ sName ] = new ODecimalType( sName, css::xsd::DataTypeClass::DECIMAL );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_FLOAT);
+ m_aRepository[ sName ] = new ODecimalType( sName, css::xsd::DataTypeClass::FLOAT );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DOUBLE);
+ m_aRepository[ sName ] = new ODecimalType( sName, css::xsd::DataTypeClass::DOUBLE );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DATE);
+ m_aRepository[ sName ] = new ODateType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_TIME);
+ m_aRepository[ sName ] = new OTimeType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DATETIME);
+ m_aRepository[ sName ] = new ODateTimeType( sName );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_YEAR);
+ m_aRepository[ sName ] = new OShortIntegerType( sName, css::xsd::DataTypeClass::gYear );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_MONTH);
+ m_aRepository[ sName ] = new OShortIntegerType( sName, css::xsd::DataTypeClass::gMonth );
+
+ sName = ResourceManager::loadString(RID_STR_DATATYPE_DAY);
+ m_aRepository[ sName ] = new OShortIntegerType( sName, css::xsd::DataTypeClass::gDay );
+ }
+
+
+ ODataTypeRepository::~ODataTypeRepository( )
+ {
+ }
+
+
+ ODataTypeRepository::Repository::iterator ODataTypeRepository::implLocate( const OUString& _rName, bool _bAllowMiss )
+ {
+ Repository::iterator aTypePos = m_aRepository.find( _rName );
+ if ( aTypePos == m_aRepository.end() && !_bAllowMiss )
+ throw NoSuchElementException( OUString(), *this );
+
+ return aTypePos;
+ }
+
+
+ Reference< XDataType > SAL_CALL ODataTypeRepository::getBasicDataType( sal_Int16 dataTypeClass )
+ {
+ Reference< XDataType > xReturn;
+
+ for ( Repository::const_iterator lookup = m_aRepository.begin();
+ ( lookup != m_aRepository.end() ) && ! xReturn.is();
+ ++lookup
+ )
+ {
+ if ( lookup->second->getIsBasic() && ( lookup->second->getTypeClass() == dataTypeClass ) )
+ xReturn = lookup->second.get();
+ }
+
+ if ( !xReturn.is() )
+ throw NoSuchElementException( OUString(), *this );
+
+ return xReturn;
+ }
+
+
+ Reference< XDataType > SAL_CALL ODataTypeRepository::cloneDataType( const OUString& sourceName, const OUString& newName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Repository::iterator aTypePos = implLocate( newName, true );
+ if ( aTypePos != m_aRepository.end() )
+ throw ElementExistException( OUString(), *this );
+
+ aTypePos = implLocate( sourceName );
+ rtl::Reference<OXSDDataType> pClone = aTypePos->second->clone( newName );
+ m_aRepository[ newName ] = pClone;
+
+ return pClone;
+ }
+
+
+ void SAL_CALL ODataTypeRepository::revokeDataType( const OUString& typeName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Repository::iterator aTypePos = implLocate( typeName );
+ if ( aTypePos->second->getIsBasic() )
+ // "This is a built-in type and cannot be removed."
+ throw VetoException(ResourceManager::loadString(RID_STR_XFORMS_CANT_REMOVE_TYPE), *this );
+
+ m_aRepository.erase( aTypePos );
+ }
+
+
+ Reference< XDataType > SAL_CALL ODataTypeRepository::getDataType( const OUString& typeName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return implLocate( typeName )->second;
+ }
+
+
+ Reference< XEnumeration > SAL_CALL ODataTypeRepository::createEnumeration( )
+ {
+ return new ::comphelper::OEnumerationByName( this );
+ }
+
+
+ Any SAL_CALL ODataTypeRepository::getByName( const OUString& aName )
+ {
+ return Any( getDataType( aName ) );
+ }
+
+
+ Sequence< OUString > SAL_CALL ODataTypeRepository::getElementNames( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return comphelper::mapKeysToSequence( m_aRepository );
+ }
+
+
+ sal_Bool SAL_CALL ODataTypeRepository::hasByName( const OUString& aName )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_aRepository.find( aName ) != m_aRepository.end();
+ }
+
+
+ Type SAL_CALL ODataTypeRepository::getElementType( )
+ {
+ return cppu::UnoType<XDataType>::get();
+ }
+
+
+ sal_Bool SAL_CALL ODataTypeRepository::hasElements( )
+ {
+ return !m_aRepository.empty();
+ }
+
+
+ // type specific implementation of registerProperties, using explicit
+ // template instantiations
+
+ template<>
+ void OValueLimitedType<css::util::Date>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_DATE, PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_DATE, PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_DATE, PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_DATE, PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<css::util::Time>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_TIME, PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_TIME, PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_TIME, PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_TIME, PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<css::util::DateTime>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_DATE_TIME, PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<double>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_DOUBLE, PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+ template<>
+ void OValueLimitedType<sal_Int16>::registerProperties()
+ {
+ OValueLimitedType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_INCLUSIVE_INT, PROPERTY_ID_XSD_MAX_INCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_EXCLUSIVE_INT, PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxExclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_INCLUSIVE_INT, PROPERTY_ID_XSD_MIN_INCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinInclusive, cppu::UnoType<ValueType>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_EXCLUSIVE_INT, PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinExclusive, cppu::UnoType<ValueType>::get() );
+ }
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatyperepository.hxx b/forms/source/xforms/datatyperepository.hxx
new file mode 100644
index 000000000..cd5ea944d
--- /dev/null
+++ b/forms/source/xforms/datatyperepository.hxx
@@ -0,0 +1,83 @@
+/* -*- 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/xforms/XDataTypeRepository.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <map>
+
+
+namespace xforms
+{
+
+ class OXSDDataType;
+
+ typedef ::cppu::WeakImplHelper < css::xforms::XDataTypeRepository
+ > ODataTypeRepository_Base;
+ class ODataTypeRepository : public ODataTypeRepository_Base
+ {
+ private:
+ typedef ::rtl::Reference< OXSDDataType > DataType;
+ typedef ::std::map< OUString, DataType > Repository;
+
+ ::osl::Mutex m_aMutex;
+ Repository m_aRepository;
+
+ public:
+ ODataTypeRepository( );
+
+ protected:
+ virtual ~ODataTypeRepository( ) override;
+
+ // XDataTypeRepository
+ virtual css::uno::Reference< css::xsd::XDataType > SAL_CALL getBasicDataType( sal_Int16 dataTypeClass ) override;
+ virtual css::uno::Reference< css::xsd::XDataType > SAL_CALL cloneDataType( const OUString& sourceName, const OUString& newName ) override;
+ virtual void SAL_CALL revokeDataType( const OUString& typeName ) override;
+ virtual css::uno::Reference< css::xsd::XDataType > SAL_CALL getDataType( const OUString& typeName ) override;
+
+ // XEnumerationAccess (base of XDataTypeRepository)
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override;
+
+ // XNameAccess (base of XDataTypeRepository)
+ 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 (base of XEnumerationAccess and XNameAccess)
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ private:
+ ODataTypeRepository( const ODataTypeRepository& ) = delete;
+ ODataTypeRepository& operator=( const ODataTypeRepository& ) = delete;
+
+ private:
+ /** locates the type with the given name in our repository, or throws an exception if there is no such type
+ */
+ Repository::iterator implLocate( const OUString& _rName, bool _bAllowMiss = false );
+ };
+
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatypes.cxx b/forms/source/xforms/datatypes.cxx
new file mode 100644
index 000000000..d4578013c
--- /dev/null
+++ b/forms/source/xforms/datatypes.cxx
@@ -0,0 +1,976 @@
+/* -*- 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 "datatypes.hxx"
+#include "resourcehelper.hxx"
+#include <frm_strings.hxx>
+#include <property.hxx>
+#include <strings.hrc>
+#include "convert.hxx"
+
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+#include <com/sun/star/xsd/WhiteSpaceTreatment.hpp>
+#include <tools/datetime.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+
+namespace xforms
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::util::Date;
+ using ::com::sun::star::util::Time;
+ using ::com::sun::star::util::DateTime;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::beans::XVetoableChangeListener;
+
+ using ::com::sun::star::beans::PropertyAttribute::BOUND;
+ using ::com::sun::star::beans::PropertyAttribute::READONLY;
+
+ using namespace ::com::sun::star::xsd;
+ using namespace ::frm;
+ U_NAMESPACE_USE
+
+ OXSDDataType::OXSDDataType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OXSDDataType_PBase( m_aBHelper )
+ ,m_bIsBasic( true )
+ ,m_nTypeClass( _nTypeClass )
+ ,m_sName( _rName )
+ ,m_nWST( WhiteSpaceTreatment::Preserve )
+ ,m_bPatternMatcherDirty( true )
+ {
+ }
+
+
+ OXSDDataType::~OXSDDataType()
+ {
+ }
+
+
+ void OXSDDataType::registerProperties()
+ {
+ registerProperty( PROPERTY_NAME, PROPERTY_ID_NAME, BOUND, &m_sName, cppu::UnoType<decltype(m_sName)>::get() );
+ registerProperty( PROPERTY_XSD_WHITESPACE, PROPERTY_ID_XSD_WHITESPACE, BOUND, &m_nWST, cppu::UnoType<cppu::UnoUnsignedShortType>::get() );
+ registerProperty( PROPERTY_XSD_PATTERN, PROPERTY_ID_XSD_PATTERN, BOUND, &m_sPattern, cppu::UnoType<decltype(m_sPattern)>::get() );
+
+ registerProperty( PROPERTY_XSD_IS_BASIC, PROPERTY_ID_XSD_IS_BASIC, READONLY, &m_bIsBasic, cppu::UnoType<decltype(m_bIsBasic)>::get() );
+ registerProperty( PROPERTY_XSD_TYPE_CLASS, PROPERTY_ID_XSD_TYPE_CLASS, READONLY, &m_nTypeClass, cppu::UnoType<decltype(m_nTypeClass)>::get() );
+ }
+
+
+ void OXSDDataType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ m_bIsBasic = false;
+ m_nTypeClass = _rCloneSource.m_nTypeClass;
+ m_sPattern = _rCloneSource.m_sPattern;
+ m_nWST = _rCloneSource.m_nWST;
+ }
+
+
+ rtl::Reference<OXSDDataType> OXSDDataType::clone( const OUString& _rNewName ) const
+ {
+ rtl::Reference<OXSDDataType> pClone = createClone( _rNewName );
+ pClone->initializeClone( *this );
+ return pClone;
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OXSDDataType, OXSDDataType_Base, ::comphelper::OPropertyContainer )
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OXSDDataType, OXSDDataType_Base, ::comphelper::OPropertyContainer )
+
+ OUString SAL_CALL OXSDDataType::getName( )
+ {
+ return m_sName;
+ }
+
+
+ void SAL_CALL OXSDDataType::setName( const OUString& aName )
+ {
+ // TODO: check the name for conflicts in the repository
+ setFastPropertyValue( PROPERTY_ID_NAME, Any(aName) );
+ SAL_WARN_IF( m_sName != aName, "forms.misc", "OXSDDataType::setName: inconsistency!" );
+ }
+
+
+ OUString SAL_CALL OXSDDataType::getPattern()
+ {
+ return m_sPattern;
+ }
+
+
+ void SAL_CALL OXSDDataType::setPattern( const OUString& _pattern )
+ {
+ setFastPropertyValue( PROPERTY_ID_XSD_PATTERN, Any(_pattern) );
+ SAL_WARN_IF( m_sPattern != _pattern, "forms.misc", "OXSDDataType::setPattern: inconsistency!" );
+ }
+
+
+ sal_Int16 SAL_CALL OXSDDataType::getWhiteSpaceTreatment()
+ {
+ return m_nWST;
+ }
+
+
+ void SAL_CALL OXSDDataType::setWhiteSpaceTreatment( sal_Int16 _whitespacetreatment )
+ {
+ setFastPropertyValue( PROPERTY_ID_XSD_WHITESPACE, Any(_whitespacetreatment) );
+ SAL_WARN_IF( m_nWST != _whitespacetreatment, "forms.misc", "OXSDDataType::setWhiteSpaceTreatment: inconsistency!" );
+ }
+
+
+ sal_Bool SAL_CALL OXSDDataType::getIsBasic()
+ {
+ return m_bIsBasic;
+ }
+
+
+ sal_Int16 SAL_CALL OXSDDataType::getTypeClass()
+ {
+ return m_nTypeClass;
+ }
+
+
+ sal_Bool OXSDDataType::validate( const OUString& sValue )
+ {
+ return bool(_validate( sValue ));
+ }
+
+
+ OUString OXSDDataType::explainInvalid( const OUString& sValue )
+ {
+ // get reason
+ TranslateId pReason = _validate( sValue );
+
+ // get resource and return localized string
+ return (!pReason)
+ ? OUString()
+ : getResource( pReason, sValue,
+ _explainInvalid( pReason ) );
+ }
+
+ OUString OXSDDataType::_explainInvalid(TranslateId rReason)
+ {
+ if ( RID_STR_XFORMS_PATTERN_DOESNT_MATCH == rReason )
+ {
+ OSL_ENSURE( !m_sPattern.isEmpty(), "OXSDDataType::_explainInvalid: how can this error occur without a regular expression?" );
+ return m_sPattern;
+ }
+ return OUString();
+ }
+
+ namespace
+ {
+ void lcl_initializePatternMatcher( ::std::unique_ptr< RegexMatcher >& _rpMatcher, const OUString& _rPattern )
+ {
+ UErrorCode nMatchStatus = U_ZERO_ERROR;
+ UnicodeString aIcuPattern( reinterpret_cast<const UChar *>(_rPattern.getStr()), _rPattern.getLength() );
+ _rpMatcher.reset( new RegexMatcher( aIcuPattern, 0, nMatchStatus ) );
+ OSL_ENSURE( U_SUCCESS( nMatchStatus ), "lcl_initializePatternMatcher: invalid pattern property!" );
+ // if asserts, then something changed our pattern without going to convertFastPropertyValue/checkPropertySanity
+ }
+
+ bool lcl_matchString( RegexMatcher& _rMatcher, const OUString& _rText )
+ {
+ UErrorCode nMatchStatus = U_ZERO_ERROR;
+ UnicodeString aInput( reinterpret_cast<const UChar *>(_rText.getStr()), _rText.getLength() );
+ _rMatcher.reset( aInput );
+ if ( _rMatcher.matches( nMatchStatus ) )
+ {
+ int32_t nStart = _rMatcher.start( nMatchStatus );
+ int32_t nEnd = _rMatcher.end ( nMatchStatus );
+ if ( ( nStart == 0 ) && ( nEnd == _rText.getLength() ) )
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ TranslateId OXSDDataType::_validate( const OUString& _rValue )
+ {
+ // care for the regular expression
+ if ( !m_sPattern.isEmpty() )
+ {
+ // ensure our pattern matcher is up to date
+ if ( m_bPatternMatcherDirty )
+ {
+ lcl_initializePatternMatcher( m_pPatternMatcher, m_sPattern );
+ m_bPatternMatcherDirty = false;
+ }
+
+ // let it match the string
+ if (!lcl_matchString(*m_pPatternMatcher, _rValue))
+ return RID_STR_XFORMS_PATTERN_DOESNT_MATCH;
+ }
+
+ return {};
+ }
+
+
+ sal_Bool OXSDDataType::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ // let the base class do the conversion
+ if ( !OXSDDataType_PBase::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue ) )
+ return false;
+
+ // sanity checks
+ OUString sErrorMessage;
+ if ( !checkPropertySanity( _nHandle, _rConvertedValue, sErrorMessage ) )
+ {
+ IllegalArgumentException aException;
+ aException.Message = sErrorMessage;
+ aException.Context = *this;
+ throw aException;
+ }
+
+ return true;
+ }
+
+
+ void SAL_CALL OXSDDataType::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ OXSDDataType_PBase::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+ if ( _nHandle == PROPERTY_ID_XSD_PATTERN )
+ m_bPatternMatcherDirty = true;
+ }
+
+
+ bool OXSDDataType::checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage )
+ {
+ if ( _nHandle == PROPERTY_ID_XSD_PATTERN )
+ {
+ OUString sPattern;
+ OSL_VERIFY( _rNewValue >>= sPattern );
+
+ UnicodeString aIcuPattern( reinterpret_cast<const UChar *>(sPattern.getStr()), sPattern.getLength() );
+ UErrorCode nMatchStatus = U_ZERO_ERROR;
+ RegexMatcher aMatcher( aIcuPattern, 0, nMatchStatus );
+ if ( U_FAILURE( nMatchStatus ) )
+ {
+ _rErrorMessage = "This is no valid pattern.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ void SAL_CALL OXSDDataType::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+ {
+ OXSDDataType_PBase::setPropertyValue( aPropertyName, aValue );
+ }
+
+
+ Any SAL_CALL OXSDDataType::getPropertyValue( const OUString& PropertyName )
+ {
+ return OXSDDataType_PBase::getPropertyValue( PropertyName );
+ }
+
+
+ void SAL_CALL OXSDDataType::addPropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener )
+ {
+ OXSDDataType_PBase::addPropertyChangeListener( aPropertyName, xListener );
+ }
+
+
+ void SAL_CALL OXSDDataType::removePropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& aListener )
+ {
+ OXSDDataType_PBase::removePropertyChangeListener( aPropertyName, aListener );
+ }
+
+
+ void SAL_CALL OXSDDataType::addVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener )
+ {
+ OXSDDataType_PBase::addVetoableChangeListener( PropertyName, aListener );
+ }
+
+
+ void SAL_CALL OXSDDataType::removeVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener )
+ {
+ OXSDDataType_PBase::removeVetoableChangeListener( PropertyName, aListener );
+ }
+
+ OValueLimitedType_Base::OValueLimitedType_Base( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OXSDDataType( _rName, _nTypeClass )
+ ,m_fCachedMaxInclusive( 0 )
+ ,m_fCachedMaxExclusive( 0 )
+ ,m_fCachedMinInclusive( 0 )
+ ,m_fCachedMinExclusive( 0 )
+ {
+ }
+
+
+ void OValueLimitedType_Base::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OXSDDataType::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OValueLimitedType_Base& >( _rCloneSource ) );
+ }
+
+
+ void OValueLimitedType_Base::initializeTypedClone( const OValueLimitedType_Base& _rCloneSource )
+ {
+ m_aMaxInclusive = _rCloneSource.m_aMaxInclusive;
+ m_aMaxExclusive = _rCloneSource.m_aMaxExclusive;
+ m_aMinInclusive = _rCloneSource.m_aMinInclusive;
+ m_aMinExclusive = _rCloneSource.m_aMinExclusive;
+ m_fCachedMaxInclusive = _rCloneSource.m_fCachedMaxInclusive;
+ m_fCachedMaxExclusive = _rCloneSource.m_fCachedMaxExclusive;
+ m_fCachedMinInclusive = _rCloneSource.m_fCachedMinInclusive;
+ m_fCachedMinExclusive = _rCloneSource.m_fCachedMinExclusive;
+ }
+
+
+ void SAL_CALL OValueLimitedType_Base::setFastPropertyValue_NoBroadcast(
+ sal_Int32 _nHandle, const css::uno::Any& _rValue )
+ {
+ OXSDDataType::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
+
+ // if one of our limit properties has been set, translate it into a double
+ // value, for later efficient validation
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MAX_INCLUSIVE_DATE_TIME:
+ if ( m_aMaxInclusive.hasValue() )
+ normalizeValue( m_aMaxInclusive, m_fCachedMaxInclusive );
+ else
+ m_fCachedMaxInclusive = 0;
+ break;
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MAX_EXCLUSIVE_DATE_TIME:
+ if ( m_aMaxExclusive.hasValue() )
+ normalizeValue( m_aMaxExclusive, m_fCachedMaxExclusive );
+ else
+ m_fCachedMaxExclusive = 0;
+ break;
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MIN_INCLUSIVE_DATE_TIME:
+ if ( m_aMinInclusive.hasValue() )
+ normalizeValue( m_aMinInclusive, m_fCachedMinInclusive );
+ else
+ m_fCachedMinInclusive = 0;
+ break;
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_INT:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DOUBLE:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_TIME:
+ case PROPERTY_ID_XSD_MIN_EXCLUSIVE_DATE_TIME:
+ if ( m_aMinExclusive.hasValue() )
+ normalizeValue( m_aMinExclusive, m_fCachedMinExclusive );
+ else
+ m_fCachedMinExclusive = 0;
+ break;
+ }
+ }
+
+
+ bool OValueLimitedType_Base::_getValue( const OUString& rValue, double& fValue )
+ {
+ // convert to double
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nEnd;
+ double f = ::rtl::math::stringToDouble(
+ rValue, '.', u'\0', &eStatus, &nEnd );
+
+ // error checking...
+ bool bReturn = false;
+ if( eStatus == rtl_math_ConversionStatus_Ok
+ && nEnd == rValue.getLength() )
+ {
+ bReturn = true;
+ fValue = f;
+ }
+ return bReturn;
+ }
+
+ TranslateId OValueLimitedType_Base::_validate( const OUString& rValue )
+ {
+ TranslateId pReason = OXSDDataType::_validate( rValue );
+ if (!pReason)
+ {
+
+ // convert value and check format
+ double f;
+ if( ! _getValue( rValue, f ) )
+ pReason = RID_STR_XFORMS_VALUE_IS_NOT_A;
+
+ // check range
+ else if( ( m_aMaxInclusive.hasValue() ) && f > m_fCachedMaxInclusive )
+ pReason = RID_STR_XFORMS_VALUE_MAX_INCL;
+ else if( ( m_aMaxExclusive.hasValue() ) && f >= m_fCachedMaxExclusive )
+ pReason = RID_STR_XFORMS_VALUE_MAX_EXCL;
+ else if( ( m_aMinInclusive.hasValue() ) && f < m_fCachedMinInclusive )
+ pReason = RID_STR_XFORMS_VALUE_MIN_INCL;
+ else if( ( m_aMinExclusive.hasValue() ) && f <= m_fCachedMinExclusive )
+ pReason = RID_STR_XFORMS_VALUE_MIN_EXCL;
+ }
+ return pReason;
+ }
+
+ OUString OValueLimitedType_Base::_explainInvalid(TranslateId rReason)
+ {
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_IS_NOT_A)
+ sInfo.append( getName() );
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_INCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMaxInclusive ) );
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_EXCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMaxExclusive ) );
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_INCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMinInclusive ) );
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_EXCL)
+ sInfo.append( typedValueAsHumanReadableString( m_aMinExclusive ) );
+ return sInfo.makeStringAndClear();
+ }
+
+ OStringType::OStringType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OStringType_Base( _rName, _nTypeClass )
+ {
+ }
+
+
+ void OStringType::registerProperties()
+ {
+ OStringType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_LENGTH, PROPERTY_ID_XSD_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aLength, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MIN_LENGTH, PROPERTY_ID_XSD_MIN_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMinLength, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_MAX_LENGTH, PROPERTY_ID_XSD_MAX_LENGTH, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aMaxLength, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ rtl::Reference<OXSDDataType> OStringType::createClone( const OUString& _rName ) const
+ {
+ return new OStringType( _rName, getTypeClass() );
+ }
+ void OStringType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OStringType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OStringType& >( _rCloneSource ) );
+ }
+
+
+
+ void OStringType::initializeTypedClone( const OStringType& _rCloneSource )
+ {
+ m_aLength = _rCloneSource.m_aLength;
+ m_aMinLength = _rCloneSource.m_aMinLength;
+ m_aMaxLength = _rCloneSource.m_aMaxLength;
+ }
+
+
+ bool OStringType::checkPropertySanity( sal_Int32 _nHandle, const Any& _rNewValue, OUString& _rErrorMessage )
+ {
+ // let the base class do the conversion
+ if ( !OStringType_Base::checkPropertySanity( _nHandle, _rNewValue, _rErrorMessage ) )
+ return false;
+
+ _rErrorMessage.clear();
+ switch ( _nHandle )
+ {
+ case PROPERTY_ID_XSD_LENGTH:
+ case PROPERTY_ID_XSD_MIN_LENGTH:
+ case PROPERTY_ID_XSD_MAX_LENGTH:
+ {
+ sal_Int32 nValue( 0 );
+ OSL_VERIFY( _rNewValue >>= nValue );
+ if ( nValue <= 0 )
+ _rErrorMessage = "Length limits must denote positive integer values.";
+ // TODO/eforms: localize the error message
+ }
+ break;
+ }
+
+ return _rErrorMessage.isEmpty();
+ }
+
+
+ TranslateId OStringType::_validate( const OUString& rValue )
+ {
+ // check regexp, whitespace etc. in parent class
+ TranslateId pReason = OStringType_Base::_validate( rValue );
+
+ if (!pReason)
+ {
+ // check string constraints
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nLimit = 0;
+ if ( m_aLength >>= nLimit )
+ {
+ if ( nLimit != nLength )
+ pReason = RID_STR_XFORMS_VALUE_LENGTH;
+ }
+ else
+ {
+ if ( ( m_aMaxLength >>= nLimit ) && ( nLength > nLimit ) )
+ pReason = RID_STR_XFORMS_VALUE_MAX_LENGTH;
+ else if ( ( m_aMinLength >>= nLimit ) && ( nLength < nLimit ) )
+ pReason = RID_STR_XFORMS_VALUE_MIN_LENGTH;
+ }
+ }
+ return pReason;
+ }
+
+ OUString OStringType::_explainInvalid(TranslateId rReason)
+ {
+ sal_Int32 nValue = 0;
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_LENGTH)
+ {
+ if( m_aLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_MAX_LENGTH)
+ {
+ if( m_aMaxLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_MIN_LENGTH)
+ {
+ if( m_aMinLength >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason)
+ {
+ sInfo.append(OStringType_Base::_explainInvalid(rReason));
+ }
+ return sInfo.makeStringAndClear();
+ }
+
+ OBooleanType::OBooleanType( const OUString& _rName )
+ :OBooleanType_Base( _rName, DataTypeClass::BOOLEAN )
+ {
+ }
+
+ rtl::Reference<OXSDDataType> OBooleanType::createClone( const OUString& _rName ) const
+ {
+ return new OBooleanType( _rName );
+ }
+
+ void OBooleanType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OBooleanType_Base::initializeClone( _rCloneSource );
+ }
+
+ TranslateId OBooleanType::_validate( const OUString& sValue )
+ {
+ TranslateId pInvalidityReason = OBooleanType_Base::_validate( sValue );
+ if ( pInvalidityReason )
+ return pInvalidityReason;
+
+ bool bValid = sValue == "0" || sValue == "1" || sValue == "true" || sValue == "false";
+ return bValid ? TranslateId() : RID_STR_XFORMS_INVALID_VALUE;
+ }
+
+ OUString OBooleanType::_explainInvalid(TranslateId rReason)
+ {
+ return !rReason ? OUString() : getName();
+ }
+
+ ODecimalType::ODecimalType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :ODecimalType_Base( _rName, _nTypeClass )
+ {
+ }
+
+ rtl::Reference<OXSDDataType> ODecimalType::createClone( const OUString& _rName ) const
+ {
+ return new ODecimalType( _rName, getTypeClass() );
+ }
+ void ODecimalType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ ODecimalType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const ODecimalType& >( _rCloneSource ) );
+ }
+
+ void ODecimalType::initializeTypedClone( const ODecimalType& _rCloneSource )
+ {
+ m_aTotalDigits = _rCloneSource.m_aTotalDigits;
+ m_aFractionDigits = _rCloneSource.m_aFractionDigits;
+ }
+
+
+ void ODecimalType::registerProperties()
+ {
+ ODecimalType_Base::registerProperties();
+
+ registerMayBeVoidProperty( PROPERTY_XSD_TOTAL_DIGITS, PROPERTY_ID_XSD_TOTAL_DIGITS, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aTotalDigits, cppu::UnoType<sal_Int32>::get() );
+
+ registerMayBeVoidProperty( PROPERTY_XSD_FRACTION_DIGITS, PROPERTY_ID_XSD_FRACTION_DIGITS, css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::MAYBEVOID,
+ &m_aFractionDigits, cppu::UnoType<sal_Int32>::get() );
+ }
+
+
+ // validate decimals and return code for which facets failed
+ // to be used by: ODecimalType::validate and ODecimalType::explainInvalid
+ TranslateId ODecimalType::_validate( const OUString& rValue )
+ {
+ TranslateId pReason = ODecimalType_Base::_validate( rValue );
+
+ // check digits (if no other cause is available so far)
+ if (!pReason)
+ {
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 n = 0;
+ sal_Int32 nTotalDigits = 0;
+ sal_Int32 nFractionDigits = 0;
+ const sal_Unicode* pValue = rValue.getStr();
+ for( ; n < nLength && pValue[n] != '.'; n++ )
+ if( pValue[n] >= '0'
+ && pValue[n] <= '9')
+ nTotalDigits++;
+ for( ; n < nLength; n++ )
+ if( pValue[n] >= '0'
+ && pValue[n] <= '9')
+ nFractionDigits++;
+ nTotalDigits += nFractionDigits;
+
+ sal_Int32 nValue = 0;
+ if( ( m_aTotalDigits >>= nValue ) && nTotalDigits > nValue )
+ pReason = RID_STR_XFORMS_VALUE_TOTAL_DIGITS;
+ else if( ( m_aFractionDigits >>= nValue ) &&
+ ( nFractionDigits > nValue ) )
+ pReason = RID_STR_XFORMS_VALUE_FRACTION_DIGITS;
+ }
+
+ return pReason;
+ }
+
+ OUString ODecimalType::_explainInvalid(TranslateId rReason)
+ {
+ sal_Int32 nValue = 0;
+ OUStringBuffer sInfo;
+ if (rReason == RID_STR_XFORMS_VALUE_TOTAL_DIGITS)
+ {
+ if( m_aTotalDigits >>= nValue )
+ sInfo.append( nValue );
+ }
+ else if (rReason == RID_STR_XFORMS_VALUE_FRACTION_DIGITS)
+ {
+ if( m_aFractionDigits >>= nValue )
+ sInfo.append( nValue );
+ }
+ else
+ {
+ sInfo.append(ODecimalType_Base::_explainInvalid(rReason));
+ }
+ return sInfo.makeStringAndClear();
+ }
+
+ OUString ODecimalType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ double fValue( 0 );
+ normalizeValue( _rValue, fValue );
+ return OUString::number( fValue );
+ }
+
+
+ void ODecimalType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ OSL_VERIFY( _rValue >>= _rDoubleValue );
+ }
+
+
+ ODateType::ODateType(const OUString& _rName)
+ :ODateType_Base(_rName, DataTypeClass::DATE)
+ {
+ }
+ rtl::Reference<OXSDDataType> ODateType::createClone(const OUString& _rName) const
+ {
+ return new ODateType(_rName);
+ }
+ void ODateType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ ODateType_Base::initializeClone(_rCloneSource);
+ initializeTypedClone(static_cast< const ODateType& >(_rCloneSource));
+ }
+
+ TranslateId ODateType::_validate( const OUString& _rValue )
+ {
+ return ODateType_Base::_validate( _rValue );
+ }
+
+ bool ODateType::_getValue( const OUString& value, double& fValue )
+ {
+ Any aTypeValue = Convert::get().toAny( value, getCppuType() );
+
+ Date aValue;
+ if ( !( aTypeValue >>= aValue ) )
+ return false;
+
+ ::Date aToolsDate( aValue.Day, aValue.Month, aValue.Year );
+ fValue = aToolsDate.GetDate();
+ return true;
+ }
+
+
+ OUString ODateType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ OSL_PRECOND( _rValue.getValueType().equals( getCppuType() ), "ODateType::typedValueAsHumanReadableString: unexpected type" );
+ return Convert::get().toXSD( _rValue );
+ }
+
+
+ void ODateType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ Date aValue;
+ OSL_VERIFY( _rValue >>= aValue );
+ ::Date aToolsDate( aValue.Day, aValue.Month, aValue.Year );
+ _rDoubleValue = aToolsDate.GetDate();
+ }
+
+
+ OTimeType::OTimeType(const OUString& _rName)
+ :OTimeType_Base(_rName, DataTypeClass::TIME)
+ {
+ }
+ rtl::Reference<OXSDDataType> OTimeType::createClone(const OUString& _rName) const
+ {
+ return new OTimeType(_rName);
+ }
+ void OTimeType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OTimeType_Base::initializeClone(_rCloneSource);
+ initializeTypedClone(static_cast< const OTimeType& >(_rCloneSource));
+ }
+
+ TranslateId OTimeType::_validate( const OUString& _rValue )
+ {
+ return OTimeType_Base::_validate( _rValue );
+ }
+
+ bool OTimeType::_getValue( const OUString& value, double& fValue )
+ {
+ Any aTypedValue = Convert::get().toAny( value, getCppuType() );
+
+ css::util::Time aValue;
+ if ( !( aTypedValue >>= aValue ) )
+ return false;
+
+ ::tools::Time aToolsTime( aValue );
+ // no loss/rounding; IEEE 754 double-precision floating-point
+ // has a mantissa of 53 bits; we need at the very most 50 bits:
+ // format of aToolsTime.GetTime() is (in decimal) hhmmssnnnnnnnnn
+ // and 999999999999999 = 0x38D7EA4C67FFF
+ // in reality I doubt we need (much) more than
+ // 240000000000000 = 0x0DA475ABF0000
+ // that is 48 bits
+ fValue = aToolsTime.GetTime();
+ return true;
+ }
+
+
+ OUString OTimeType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ OSL_PRECOND( _rValue.getValueType().equals( getCppuType() ), "OTimeType::typedValueAsHumanReadableString: unexpected type" );
+ return Convert::get().toXSD( _rValue );
+ }
+
+
+ void OTimeType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ css::util::Time aValue;
+ OSL_VERIFY( _rValue >>= aValue );
+ ::tools::Time aToolsTime( aValue );
+ _rDoubleValue = aToolsTime.GetTime();
+ }
+
+
+ ODateTimeType::ODateTimeType(const OUString& _rName)
+ :ODateTimeType_Base(_rName, DataTypeClass::DATETIME)
+ {
+ }
+ rtl::Reference<OXSDDataType> ODateTimeType::createClone(const OUString& _rName) const
+ {
+ return new ODateTimeType(_rName);
+ }
+ void ODateTimeType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ ODateTimeType_Base::initializeClone(_rCloneSource);
+ initializeTypedClone(static_cast< const ODateTimeType& >(_rCloneSource));
+ }
+
+ TranslateId ODateTimeType::_validate( const OUString& _rValue )
+ {
+ return ODateTimeType_Base::_validate( _rValue );
+ }
+
+ namespace
+ {
+ double lcl_normalizeDateTime( const DateTime& _rValue )
+ {
+ ::DateTime aToolsValue(_rValue);
+
+ double fValue = 0;
+ // days since 1.1.1900 (which is relatively arbitrary but fixed date)
+ fValue += ::Date( aToolsValue ) - ::Date( 1, 1, 1900 );
+ // time
+ fValue += aToolsValue.GetTimeInDays();
+ return fValue;
+ }
+ }
+
+
+ bool ODateTimeType::_getValue( const OUString& value, double& fValue )
+ {
+ Any aTypedValue = Convert::get().toAny( value, getCppuType() );
+
+ DateTime aValue;
+ if ( !( aTypedValue >>= aValue ) )
+ return false;
+
+ fValue = lcl_normalizeDateTime( aValue );
+ return true;
+ }
+
+
+ OUString ODateTimeType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ OSL_PRECOND( _rValue.getValueType().equals( getCppuType() ), "OTimeType::typedValueAsHumanReadableString: unexpected type" );
+ OUString sString = Convert::get().toXSD( _rValue );
+
+ // ISO 8601 notation has a "T" to separate between date and time. Our only concession
+ // to the "human readable" in the method name is to replace this T with a whitespace.
+ OSL_ENSURE( sString.indexOf( 'T' ) != -1, "ODateTimeType::typedValueAsHumanReadableString: hmm - no ISO notation?" );
+ return sString.replace( 'T', ' ' );
+ }
+
+
+ void ODateTimeType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ DateTime aValue;
+ OSL_VERIFY( _rValue >>= aValue );
+ _rDoubleValue = lcl_normalizeDateTime( aValue );
+ }
+
+ OShortIntegerType::OShortIntegerType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OShortIntegerType_Base( _rName, _nTypeClass )
+ {
+ }
+
+ rtl::Reference<OXSDDataType> OShortIntegerType::createClone( const OUString& _rName ) const
+ {
+ return new OShortIntegerType( _rName, getTypeClass() );
+ }
+ void OShortIntegerType::initializeClone( const OXSDDataType& _rCloneSource )
+ {
+ OShortIntegerType_Base::initializeClone( _rCloneSource );
+ initializeTypedClone( static_cast< const OShortIntegerType& >( _rCloneSource ) );
+ }
+
+ bool OShortIntegerType::_getValue( const OUString& value, double& fValue )
+ {
+ fValue = static_cast<double>(static_cast<sal_Int16>(value.toInt32()));
+ // TODO/eforms
+ // this does not care for values which do not fit into a sal_Int16, but simply
+ // cuts them down. A better implementation here should probably return <FALSE/>
+ // for those values.
+ // Else, we may have a situation where the UI claims an input to be valid
+ // (say "12345678"), while internally, and at submission time, this is cut to
+ // some smaller value.
+
+ // Additionally, this of course does not care for strings which are no numbers...
+ return true;
+ }
+
+
+ OUString OShortIntegerType::typedValueAsHumanReadableString( const Any& _rValue ) const
+ {
+ sal_Int16 nValue( 0 );
+ OSL_VERIFY( _rValue >>= nValue );
+ return OUString::number( nValue );
+ }
+
+
+ void OShortIntegerType::normalizeValue( const Any& _rValue, double& _rDoubleValue ) const
+ {
+ sal_Int16 nValue( 0 );
+ OSL_VERIFY( _rValue >>= nValue );
+ _rDoubleValue = nValue;
+ }
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::ODerivedDataType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :SUPERCLASS( _rName, _nTypeClass )
+ ,m_bPropertiesRegistered( false )
+{
+}
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+::cppu::IPropertyArrayHelper* ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::createArrayHelper( ) const
+{
+ css::uno::Sequence< css::beans::Property > aProps;
+ ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+}
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::getPropertySetInfo()
+{
+ return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+}
+
+
+template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS >
+::cppu::IPropertyArrayHelper& SAL_CALL ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::getInfoHelper()
+{
+ if ( !m_bPropertiesRegistered )
+ {
+ this->registerProperties();
+ m_bPropertiesRegistered = true;
+ }
+
+ return *ODerivedDataType< CONCRETE_DATA_TYPE_IMPL, SUPERCLASS >::getArrayHelper();
+}
+
+
+template< typename VALUE_TYPE >
+OValueLimitedType< VALUE_TYPE >::OValueLimitedType( const OUString& _rName, sal_Int16 _nTypeClass )
+ :OValueLimitedType_Base( _rName, _nTypeClass )
+{
+}
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/datatypes.hxx b/forms/source/xforms/datatypes.hxx
new file mode 100644
index 000000000..bed7f2d3a
--- /dev/null
+++ b/forms/source/xforms/datatypes.hxx
@@ -0,0 +1,393 @@
+/* -*- 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/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/xsd/XDataType.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/uno3.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/broadcasthelper.hxx>
+#include <rtl/ref.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <unicode/regex.h>
+
+#include <memory>
+
+
+namespace xforms
+{
+
+
+ //= OXSDDataType
+
+ typedef ::cppu::WeakImplHelper < css::xsd::XDataType
+ > OXSDDataType_Base;
+ typedef ::comphelper::OMutexAndBroadcastHelper OXSDDataType_BBase;
+ typedef ::comphelper::OPropertyContainer OXSDDataType_PBase;
+
+ class OXSDDataType :public OXSDDataType_Base
+ ,public OXSDDataType_BBase // order matters: OMutexAndBroadcastHelper before
+ ,public OXSDDataType_PBase // OPropertyContainer
+ {
+ private:
+ // <properties>
+ bool m_bIsBasic;
+ sal_Int16 m_nTypeClass;
+ OUString m_sName;
+ OUString m_sPattern;
+ sal_uInt16 m_nWST;
+ // </properties>
+
+ ::std::unique_ptr< U_NAMESPACE_QUALIFIER RegexMatcher >
+ m_pPatternMatcher;
+ bool m_bPatternMatcherDirty;
+
+ protected:
+ sal_Int16 getTypeClass() const { return m_nTypeClass; }
+
+ private:
+ OXSDDataType( const OXSDDataType& ) = delete;
+ OXSDDataType& operator=( const OXSDDataType& ) = delete;
+
+ protected:
+ // create basic data type
+ OXSDDataType( const OUString& _rName, sal_Int16 _nTypeClass );
+ virtual ~OXSDDataType() override;
+
+ public:
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ virtual OUString SAL_CALL getName( ) override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+ virtual OUString SAL_CALL getPattern() override;
+ virtual void SAL_CALL setPattern( const OUString& _pattern ) override;
+ virtual sal_Int16 SAL_CALL getWhiteSpaceTreatment() override;
+ virtual void SAL_CALL setWhiteSpaceTreatment( sal_Int16 _whitespacetreatment ) override;
+ virtual sal_Bool SAL_CALL getIsBasic() override;
+ virtual sal_Int16 SAL_CALL getTypeClass() override;
+
+ virtual sal_Bool SAL_CALL validate( const OUString& value ) override;
+ virtual OUString SAL_CALL explainInvalid( const OUString& value ) override;
+
+ // XPropertySet - is a base of XDataType and needs to be disambiguated
+ 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;
+
+ public:
+ rtl::Reference<OXSDDataType> clone( const OUString& _rNewName ) const;
+
+ protected:
+ // XPropertySet and friends
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& _rConvertedValue, css::uno::Any& _rOldValue, sal_Int32 _nHandle, const css::uno::Any& _rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue
+ ) override;
+
+ // --- own overridables ---
+ // helper for implementing cloning of data types
+ virtual rtl::Reference<OXSDDataType> createClone( const OUString& _rName ) const = 0;
+ virtual void initializeClone( const OXSDDataType& _rCloneSource );
+
+ // helper method for validate and explainInvalid
+ virtual TranslateId _validate( const OUString& value );
+ virtual OUString _explainInvalid( TranslateId rReason );
+
+ // helper method for checking properties values which are to be set
+ virtual bool checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage );
+
+ // register properties implemented by this instance - call the base class when overriding
+ virtual void registerProperties();
+ };
+
+
+ //= helper for deriving from OXSDDataType
+
+ class OValueLimitedType_Base : public OXSDDataType
+ {
+ protected:
+ css::uno::Any m_aMaxInclusive;
+ css::uno::Any m_aMaxExclusive;
+ css::uno::Any m_aMinInclusive;
+ css::uno::Any m_aMinExclusive;
+
+ double m_fCachedMaxInclusive;
+ double m_fCachedMaxExclusive;
+ double m_fCachedMinInclusive;
+ double m_fCachedMinExclusive;
+
+ protected:
+ OValueLimitedType_Base( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ virtual void initializeClone( const OXSDDataType& _rCloneSource ) override;
+ void initializeTypedClone( const OValueLimitedType_Base& _rCloneSource );
+
+ // XPropertySet and friends
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue
+ ) override;
+
+ // OXSDDataType overridables
+ virtual bool _getValue( const OUString& value, double& fValue );
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+
+ // own overridables
+ /** translate a given value into a human-readable string
+
+ The value is guaranteed to be not <NULL/>, and is of type <member>ValueType</member>
+ */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const = 0;
+
+ /** translates a <member>ValueType</member> value into a double value
+
+ The normalization must respect the "<" and "==" relations on the value
+ space. That is, if two values are equal, their normalizations must be equal, too.
+ Similarly, if <code>foo</code> is less than <code>bar</code>, the same
+ must hold for their normalizations.
+
+ @param _rValue
+ the value to translate. Guaranteed to be not <NULL/>, and of type <member>ValueType</member>
+ @param _rDoubleValue
+ output parameter to hold the resulting double value
+ */
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const = 0;
+ };
+
+ template < typename VALUE_TYPE >
+ class OValueLimitedType : public OValueLimitedType_Base
+ {
+ protected:
+ typedef VALUE_TYPE ValueType;
+ const css::uno::Type&
+ getCppuType() const { return cppu::UnoType<ValueType>::get(); }
+
+ protected:
+ OValueLimitedType( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ // OXSDDataType overridables
+ virtual void registerProperties() override;
+ };
+
+ /** helper class for implementing interfaces derived from XDataType
+ */
+ template< typename CONCRETE_DATA_TYPE_IMPL, typename SUPERCLASS = OXSDDataType >
+ class ODerivedDataType :public SUPERCLASS
+ ,public ::comphelper::OPropertyArrayUsageHelper< CONCRETE_DATA_TYPE_IMPL >
+ {
+ private:
+ bool m_bPropertiesRegistered;
+
+ protected:
+ ODerivedDataType( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ protected:
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override;
+
+ // XPropertySet
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ };
+
+ class OBooleanType;
+ typedef ODerivedDataType< OBooleanType > OBooleanType_Base;
+ class OBooleanType : public OBooleanType_Base
+ {
+ public:
+ explicit OBooleanType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ };
+
+ class OStringType;
+ typedef ODerivedDataType< OStringType > OStringType_Base;
+ class OStringType :public OStringType_Base
+ {
+ // <properties>
+ css::uno::Any m_aLength;
+ css::uno::Any m_aMinLength;
+ css::uno::Any m_aMaxLength;
+ // </properties>
+
+ public:
+ OStringType( const OUString& _rName, sal_Int16 _nTypeClass /* = css::xsd::DataTypeClass::STRING */ );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+ void initializeTypedClone( const OStringType& _rCloneSource );
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ virtual bool checkPropertySanity( sal_Int32 _nHandle, const css::uno::Any& _rNewValue, OUString& _rErrorMessage ) override;
+ virtual void registerProperties() override;
+ };
+
+ class ODecimalType;
+ typedef ODerivedDataType< ODecimalType, OValueLimitedType< double > > ODecimalType_Base;
+ class ODecimalType : public ODecimalType_Base
+ {
+ css::uno::Any m_aTotalDigits;
+ css::uno::Any m_aFractionDigits;
+
+ public:
+ ODecimalType( const OUString& _rName, sal_Int16 _nTypeClass /* = css::xsd::DataTypeClass::DECIMAL */ );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+ void initializeTypedClone( const ODecimalType& _rCloneSource );
+
+ // OXSDDataType overridables
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual OUString _explainInvalid( TranslateId rReason ) override;
+ virtual void registerProperties() override;
+
+ // OValueLimitedType overridables
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using ODecimalType_Base::initializeTypedClone;
+ };
+
+
+ //=
+
+ class ODateType;
+ typedef ODerivedDataType< ODateType, OValueLimitedType< css::util::Date > > ODateType_Base;
+ class ODateType : public ODateType_Base
+ {
+ public:
+ explicit ODateType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ /* OXSDDataType overridables */
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ /* OValueLimitedType overridables */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using ODateType_Base::initializeTypedClone;
+ };
+
+
+ class OTimeType;
+ typedef ODerivedDataType< OTimeType, OValueLimitedType< css::util::Time > > OTimeType_Base;
+ class OTimeType : public OTimeType_Base
+ {
+ public:
+ explicit OTimeType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ /* OXSDDataType overridables */
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ /* OValueLimitedType overridables */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using OTimeType_Base::initializeTypedClone;
+ };
+
+
+
+ class ODateTimeType;
+ typedef ODerivedDataType< OTimeType, OValueLimitedType< css::util::DateTime > > ODateTimeType_Base;
+ class ODateTimeType : public ODateTimeType_Base
+ {
+ public:
+ explicit ODateTimeType( const OUString& _rName );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ /* OXSDDataType overridables */
+ virtual TranslateId _validate( const OUString& value ) override;
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ /* OValueLimitedType overridables */
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using ODateTimeType_Base::initializeTypedClone;
+ };
+
+
+ class OShortIntegerType;
+ typedef ODerivedDataType< OShortIntegerType, OValueLimitedType< sal_Int16 > > OShortIntegerType_Base;
+ class OShortIntegerType : public OShortIntegerType_Base
+ {
+ public:
+ OShortIntegerType( const OUString& _rName, sal_Int16 _nTypeClass );
+
+ protected:
+ virtual rtl::Reference<OXSDDataType> createClone(const OUString& _rName) const override;
+ virtual void initializeClone(const OXSDDataType& _rCloneSource) override;
+
+ // OXSDDataType overridables
+ virtual bool _getValue( const OUString& value, double& fValue ) override;
+
+ // OValueLimitedType overridables
+ virtual OUString typedValueAsHumanReadableString( const css::uno::Any& _rValue ) const override;
+ virtual void normalizeValue( const css::uno::Any& _rValue, double& _rDoubleValue ) const override;
+
+ private:
+ using OShortIntegerType_Base::initializeTypedClone;
+ };
+
+
+} // namespace xforms
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/enumeration.cxx b/forms/source/xforms/enumeration.cxx
new file mode 100644
index 000000000..e02881fbb
--- /dev/null
+++ b/forms/source/xforms/enumeration.cxx
@@ -0,0 +1,59 @@
+/* -*- 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 "enumeration.hxx"
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+
+using com::sun::star::container::NoSuchElementException;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::RuntimeException;
+
+Enumeration::Enumeration(XIndexAccess* pContainer)
+ : mxContainer(pContainer)
+ , mnIndex(0)
+{
+ OSL_ENSURE(mxContainer.is(), "no container?");
+}
+
+sal_Bool Enumeration::hasMoreElements()
+{
+ if (!mxContainer.is())
+ throw RuntimeException();
+
+ return mnIndex < mxContainer->getCount();
+}
+
+Any Enumeration::nextElement()
+{
+ if (!mxContainer.is())
+ throw RuntimeException();
+ if (mnIndex >= mxContainer->getCount())
+ throw NoSuchElementException();
+
+ return mxContainer->getByIndex(mnIndex++);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/enumeration.hxx b/forms/source/xforms/enumeration.hxx
new file mode 100644
index 000000000..6374e0c8e
--- /dev/null
+++ b/forms/source/xforms/enumeration.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace container { class XIndexAccess; }
+ namespace uno { class Any; }
+ namespace container { class NoSuchElementException; }
+ namespace lang { class WrappedTargetException; }
+ namespace uno { class RuntimeException; }
+}
+
+/** implement XEnumeration based on container::XIndexAccess */
+class Enumeration
+ : public cppu::WeakImplHelper<css::container::XEnumeration>
+{
+ css::uno::Reference<css::container::XIndexAccess> mxContainer;
+ sal_Int32 mnIndex;
+
+public:
+ explicit Enumeration( css::container::XIndexAccess* );
+
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+
+ virtual css::uno::Any SAL_CALL nextElement() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/evaluationcontext.hxx b/forms/source/xforms/evaluationcontext.hxx
new file mode 100644
index 000000000..145a1f2d2
--- /dev/null
+++ b/forms/source/xforms/evaluationcontext.hxx
@@ -0,0 +1,57 @@
+/* -*- 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/xml/dom/XNode.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+
+namespace xforms
+{
+
+
+/** define the context for the evaluation of an XPath expression */
+class EvaluationContext
+{
+public:
+ EvaluationContext()
+ : mxContextNode(),
+ mxModel(),
+ mxNamespaces()
+ { }
+
+ EvaluationContext(
+ const css::uno::Reference<css::xml::dom::XNode>& xContextNode,
+ const css::uno::Reference<css::xforms::XModel>& xModel,
+ const css::uno::Reference<css::container::XNameContainer>& xNamespaces )
+ : mxContextNode( xContextNode ),
+ mxModel( xModel ),
+ mxNamespaces( xNamespaces )
+ { }
+
+ css::uno::Reference<css::xml::dom::XNode> mxContextNode;
+ css::uno::Reference<css::xforms::XModel> mxModel;
+ css::uno::Reference<css::container::XNameContainer> mxNamespaces;
+};
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/mip.cxx b/forms/source/xforms/mip.cxx
new file mode 100644
index 000000000..b1759ecbe
--- /dev/null
+++ b/forms/source/xforms/mip.cxx
@@ -0,0 +1,98 @@
+/* -*- 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 "mip.hxx"
+
+namespace xforms
+{
+
+MIP::MIP()
+{
+ resetReadonly();
+ resetRequired();
+ resetRelevant();
+ resetConstraint();
+ mbHasCalculate = false;
+ resetTypeName();
+}
+
+void MIP::inherit( const MIP& rMip )
+{
+ if( ! mbHasReadonly )
+ {
+ mbHasReadonly = rMip.mbHasReadonly;
+ mbReadonly = rMip.isReadonly();
+ }
+ if( ! mbHasRequired )
+ {
+ mbHasRequired = rMip.mbHasRequired;
+ mbRequired = rMip.isRequired();
+ }
+ if( ! mbHasRelevant )
+ {
+ mbHasRelevant = rMip.mbHasRelevant;
+ mbRelevant = rMip.isRelevant();
+ }
+ if( ! mbHasConstraint )
+ {
+ mbHasConstraint = rMip.mbHasConstraint;
+ mbConstraint = rMip.isConstraint();
+ msConstraintExplanation = rMip.getConstraintExplanation();
+ }
+ if( ! mbHasCalculate )
+ {
+ mbHasCalculate = rMip.mbHasCalculate;
+ }
+ if( ! mbHasTypeName )
+ {
+ mbHasTypeName = rMip.mbHasTypeName;
+ msTypeName = rMip.getTypeName();
+ }
+}
+
+void MIP::join( const MIP& rMip )
+{
+ // TODO: inherit only inheritable MIPs...
+ inherit( rMip );
+}
+
+bool MIP::isReadonly() const { return mbHasReadonly ? mbReadonly : mbHasCalculate; }
+void MIP::setReadonly( bool b ) { mbHasReadonly = true; mbReadonly = b; }
+void MIP::resetReadonly() { mbHasReadonly = false; mbReadonly = false; }
+
+void MIP::setRequired( bool b ) { mbHasRequired = true; mbRequired = b; }
+void MIP::resetRequired() { mbHasRequired = false; mbRequired = false; }
+
+void MIP::setRelevant( bool b ) { mbHasRelevant = true; mbRelevant = b; }
+void MIP::resetRelevant() { mbHasRelevant = false; mbRelevant = true; }
+
+void MIP::setConstraint( bool b ) { mbHasConstraint = true; mbConstraint = b; msConstraintExplanation.clear(); }
+void MIP::resetConstraint() { mbHasConstraint = false; mbConstraint = true; msConstraintExplanation.clear(); }
+
+void MIP::setConstraintExplanation( const OUString& s ) { msConstraintExplanation = s; }
+
+void MIP::setHasCalculate( bool b ) { mbHasCalculate = b; }
+
+void MIP::setTypeName( const OUString& s ) { msTypeName = s; mbHasTypeName = true; }
+void MIP::resetTypeName() { msTypeName.clear(); mbHasTypeName = false; }
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/mip.hxx b/forms/source/xforms/mip.hxx
new file mode 100644
index 000000000..97b98d04e
--- /dev/null
+++ b/forms/source/xforms/mip.hxx
@@ -0,0 +1,102 @@
+/* -*- 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>
+
+namespace xforms
+{
+/** represents the XForms *m*odel *i*tem *p*roperties (MIPs) for a
+ * given XNode in the instance data at a given point in time. The
+ * values will not be updated; for updated values new MIP objects have
+ * to be created/queried. */
+class MIP
+{
+ bool mbHasReadonly;
+ bool mbReadonly;
+
+ bool mbHasRequired;
+ bool mbRequired;
+
+ bool mbHasRelevant;
+ bool mbRelevant;
+
+ bool mbHasConstraint;
+ bool mbConstraint;
+
+ bool mbHasCalculate;
+
+ bool mbHasTypeName;
+ OUString msTypeName;
+
+ OUString msConstraintExplanation;
+
+public:
+ MIP();
+
+ /// inherit from upper-level MIPs
+ void inherit(const MIP&);
+
+ /// join with same-level MIPs
+ void join(const MIP&);
+
+ // - type (static; default: xsd:string)
+ // (currently default implemented as empty string)
+ const OUString& getTypeName() const { return msTypeName; }
+ void setTypeName(const OUString&);
+ void resetTypeName();
+
+ // - readonly (computed XPath; default: false; true if calculate exists)
+ bool isReadonly() const;
+ void setReadonly(bool);
+ void resetReadonly();
+
+ // - required (computed XPath; default: false)
+ bool isRequired() const { return mbRequired; }
+ void setRequired(bool);
+ void resetRequired();
+
+ // - relevant (computed XPath; default: true)
+ bool isRelevant() const { return mbRelevant; }
+ void setRelevant(bool);
+ void resetRelevant();
+
+ // - constraint (computed XPath; default: true)
+ bool isConstraint() const { return mbConstraint; }
+ void setConstraint(bool);
+ void resetConstraint();
+
+ // explain _why_ a constraint failed
+ void setConstraintExplanation(const OUString&);
+ const OUString& getConstraintExplanation() const { return msConstraintExplanation; }
+
+ // - calculate (computed XPath; default: has none (false))
+ // (for calculate, we only store whether a calculate MIP is present;
+ // the actual calculate value is handled my changing the instance
+ // directly)
+ void setHasCalculate(bool);
+
+ // - minOccurs/maxOccurs (computed XPath; default: 0/inf)
+ // - p3ptype (static; no default)
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model.cxx b/forms/source/xforms/model.cxx
new file mode 100644
index 000000000..7001f4aaa
--- /dev/null
+++ b/forms/source/xforms/model.cxx
@@ -0,0 +1,619 @@
+/* -*- 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 "model.hxx"
+
+#include "model_helper.hxx"
+#include "unohelper.hxx"
+#include "binding.hxx"
+#include "submission.hxx"
+#include "mip.hxx"
+#include "evaluationcontext.hxx"
+#include "xmlhelper.hxx"
+#include "datatyperepository.hxx"
+#include "NameContainer.hxx"
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <algorithm>
+
+// UNO classes
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XCharacterData.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+
+using com::sun::star::lang::XUnoTunnel;
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::ucb::SimpleFileAccess;
+using com::sun::star::io::XInputStream;
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::xml::dom;
+using namespace xforms;
+
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+#define DBG_INVARIANT_TYPE(TYPE) class DBG_##TYPE { const TYPE* mpT; void check() { mpT->dbg_assertInvariant(); } public: DBG_##TYPE(const TYPE* pT) : mpT(pT) { check(); } ~DBG_##TYPE() { check(); } } _DBG_##TYPE(this);
+
+#define DBG_INVARIANT() DBG_INVARIANT_TYPE(Model)
+#else
+#define DBG_INVARIANT()
+#endif
+
+
+// The Model
+
+
+void Model::ensureAtLeastOneInstance()
+{
+ if( ! mxInstances->hasItems() )
+ {
+ // create a default instance
+ newInstance( OUString(), OUString(), true );
+ }
+}
+
+
+/** Model default constructor; create empty model */
+Model::Model() :
+ mxInstances( new InstanceCollection ),
+ mxNamespaces( new NameContainer<OUString>() ),
+ mbInitialized( false ),
+ mbExternalData( true )
+{
+ initializePropertySet();
+
+ // initialize bindings collections
+ // (not in initializer list to avoid use of incomplete 'this')
+ mxBindings = new BindingCollection( this );
+ mxSubmissions = new SubmissionCollection( this );
+
+ // invariant only holds after construction
+ DBG_INVARIANT();
+}
+
+Model::~Model() noexcept
+{
+}
+
+EvaluationContext Model::getEvaluationContext()
+{
+ // the default context is the top-level element node. A default
+ // node (instanceData' is inserted when there is no default node
+ Reference<XDocument> xInstance = getDefaultInstance();
+ Reference<XNode> xElement = xInstance->getDocumentElement();
+
+ // no element found? Then insert default element 'instanceData'
+ if( ! xElement.is() )
+ {
+ xElement.set( xInstance->createElement( "instanceData" ), UNO_QUERY_THROW );
+ xInstance->appendChild( xElement );
+ }
+
+ OSL_ENSURE( xElement.is() &&
+ xElement->getNodeType() == NodeType_ELEMENT_NODE,
+ "no element in evaluation context" );
+
+ return EvaluationContext( xElement, this, mxNamespaces );
+}
+
+
+css::uno::Sequence<sal_Int8> Model::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit aImplementationId;
+ return aImplementationId.getSeq();
+}
+
+
+void Model::setForeignSchema( const css::uno::Reference<css::xml::dom::XDocument>& rDocument )
+{
+ mxForeignSchema = rDocument;
+}
+
+
+void Model::setSchemaRef( const OUString& rSchemaRef )
+{
+ msSchemaRef = rSchemaRef;
+}
+
+
+void Model::setNamespaces( const css::uno::Reference<css::container::XNameContainer>& rNamespaces )
+{
+ if( rNamespaces.is() )
+ mxNamespaces = rNamespaces;
+}
+
+
+void Model::setExternalData( bool _bData )
+{
+ mbExternalData = _bData;
+}
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+void Model::dbg_assertInvariant() const
+{
+ assert(mxInstances && "no instances found");
+ assert(mxBindings && "no bindings element");
+ assert(mxSubmissions && "no submissions element");
+}
+#endif
+
+
+// MIP management
+void Model::addMIP( void* pTag, const XNode_t& xNode, const MIP& rMIP )
+{
+ OSL_ENSURE( pTag != nullptr, "empty tag?" );
+ OSL_ENSURE( xNode.is(), "no node" );
+
+ MIPs_t::value_type aValue( xNode, ::std::pair<void*,MIP>( pTag, rMIP ) );
+ maMIPs.insert( aValue );
+}
+
+void Model::removeMIPs( void const * pTag )
+{
+ OSL_ENSURE( pTag != nullptr, "empty tag?" );
+
+ for( MIPs_t::iterator aIter = maMIPs.begin();
+ aIter != maMIPs.end(); )
+ {
+ if( aIter->second.first == pTag )
+ {
+ aIter = maMIPs.erase( aIter );
+ }
+ else
+ ++aIter;
+ }
+}
+
+MIP Model::queryMIP( const XNode_t& xNode ) const
+{
+ // travel up inheritance chain and inherit MIPs
+ MIP aRet;
+ for( XNode_t xCurrent = xNode;
+ xCurrent.is();
+ xCurrent = xCurrent->getParentNode() )
+ {
+ // iterate over all MIPs for this node, and join MIPs
+ MIP aMIP;
+ MIPs_t::const_iterator aEnd = maMIPs.upper_bound( xCurrent );
+ MIPs_t::const_iterator aIter = maMIPs.lower_bound( xCurrent );
+ for( ; aIter != aEnd; ++aIter )
+ aMIP.join( aIter->second.second );
+
+ // inherit from current node (or set if we are at the start node)
+ if( xCurrent == xNode )
+ aRet = aMIP;
+ else
+ aRet.inherit( aMIP );
+ }
+
+ return aRet;
+}
+
+
+void Model::rebind()
+{
+ OSL_ENSURE( mxBindings, "bindings?" );
+
+ // iterate over all bindings and call update
+ sal_Int32 nCount = mxBindings->countItems();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Binding* pBind = comphelper::getFromUnoTunnel<Binding>( mxBindings->Collection<XPropertySet_t>::getItem( i ) );
+ OSL_ENSURE( pBind != nullptr, "binding?" );
+ pBind->update();
+ }
+}
+
+
+void Model::deferNotifications( bool bDefer )
+{
+ // iterate over all bindings and defer notifications
+ sal_Int32 nCount = mxBindings->countItems();
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ Binding* pBind = comphelper::getFromUnoTunnel<Binding>( mxBindings->Collection<XPropertySet_t>::getItem( i ) );
+ OSL_ENSURE( pBind != nullptr, "binding?" );
+ pBind->deferNotifications( bDefer );
+ }
+}
+
+
+bool Model::setSimpleContent( const XNode_t& xConstNode,
+ const OUString& sValue )
+{
+ OSL_ENSURE( xConstNode.is(), "need node to set data" );
+
+ bool bRet = false;
+ if( xConstNode.is() )
+ {
+ // non-const node reference so we can assign children (if necessary)
+ XNode_t xNode( xConstNode );
+
+ switch( xNode->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ {
+ // find first text node child
+ Reference<XNode> xChild;
+ for( xChild = xNode->getFirstChild();
+ xChild.is() && xChild->getNodeType() != NodeType_TEXT_NODE;
+ xChild = xChild->getNextSibling() )
+ ; // empty loop; only find first text node child
+
+ // create text node, if none is found
+ if( ! xChild.is() )
+ {
+ xChild.set(
+ xNode->getOwnerDocument()->createTextNode( OUString() ),
+ UNO_QUERY_THROW );
+ xNode->appendChild( xChild );
+ }
+ xNode = xChild;
+
+ OSL_ENSURE( xNode.is() &&
+ xNode->getNodeType() == NodeType_TEXT_NODE,
+ "text node creation failed?" );
+ [[fallthrough]]; // continue as with text node:
+ }
+
+ case NodeType_TEXT_NODE:
+ case NodeType_ATTRIBUTE_NODE:
+ {
+ // set the node value (defer notifications)
+ if( xNode->getNodeValue() != sValue )
+ {
+ deferNotifications( true );
+ xNode->setNodeValue( sValue );
+ deferNotifications( false );
+ }
+ bRet = true;
+ }
+ break;
+
+ default:
+ {
+ OSL_FAIL( "bound to unknown node type?" );
+ }
+ break;
+
+ }
+ }
+ return bRet;
+}
+
+void Model::loadInstance( sal_Int32 nInstance )
+{
+ Sequence<PropertyValue> aSequence = mxInstances->getItem( nInstance );
+
+ // find URL from instance
+ OUString sURL;
+ bool bOnce = false;
+ getInstanceData( aSequence, nullptr, nullptr, &sURL, &bOnce );
+
+ // if we have a URL, load the document and set it into the instance
+ if( sURL.isEmpty() )
+ return;
+
+ try
+ {
+ Reference<XInputStream> xInput =
+ SimpleFileAccess::create( ::comphelper::getProcessComponentContext() )->openFileRead( sURL );
+ if( xInput.is() )
+ {
+ Reference<XDocument> xInstance =
+ getDocumentBuilder()->parse( xInput );
+ if( xInstance.is() )
+ {
+ OUString sEmpty;
+ setInstanceData( aSequence, nullptr, &xInstance,
+ bOnce ? &sEmpty : &sURL, nullptr);
+ mxInstances->setItem( nInstance, aSequence );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ // couldn't load the instance -> ignore!
+ }
+}
+
+void Model::loadInstances()
+{
+ // iterate over instance array to get PropertyValue-Sequence
+ const sal_Int32 nInstances = mxInstances->countItems();
+ for( sal_Int32 nInstance = 0; nInstance < nInstances; nInstance++ )
+ {
+ loadInstance( nInstance );
+ }
+}
+
+
+bool Model::isValid() const
+{
+ bool bValid = true;
+ sal_Int32 nCount = mxBindings->countItems();
+ for( sal_Int32 i = 0; bValid && i < nCount; i++ )
+ {
+ Binding* pBind = comphelper::getFromUnoTunnel<Binding>( mxBindings->Collection<XPropertySet_t>::getItem( i ) );
+ OSL_ENSURE( pBind != nullptr, "binding?" );
+ bValid = pBind->isValid();
+ }
+ return bValid;
+}
+
+
+// implement xforms::XModel
+
+
+OUString Model::getID()
+{
+ DBG_INVARIANT();
+ return msID;
+}
+
+void Model::setID( const OUString& sID )
+{
+ DBG_INVARIANT();
+ msID = sID;
+}
+
+void Model::initialize()
+{
+ DBG_ASSERT( ! mbInitialized, "model already initialized" );
+
+ // load instances
+ loadInstances();
+
+ // let's pretend we're initialized and rebind all bindings
+ mbInitialized = true;
+ rebind();
+}
+
+void Model::rebuild()
+{
+ if( ! mbInitialized )
+ initialize();
+ else
+ rebind();
+}
+
+void Model::recalculate()
+{
+ rebind();
+}
+
+void Model::revalidate()
+{
+ // do nothing. We don't validate anyways!
+}
+
+void Model::refresh()
+{
+ rebind();
+}
+
+
+void SAL_CALL Model::submitWithInteraction(
+ const OUString& sID,
+ const css::uno::Reference<css::task::XInteractionHandler>& _rxHandler )
+{
+ DBG_INVARIANT();
+
+ if( mxSubmissions->hasItem( sID ) )
+ {
+ Submission* pSubmission =
+ comphelper::getFromUnoTunnel<Submission>( mxSubmissions->getItem( sID ) );
+ OSL_ENSURE( pSubmission != nullptr, "no submission?" );
+ OSL_ENSURE( pSubmission->getModel() == Reference<XModel>( this ),
+ "wrong model" );
+
+ // submit. All exceptions are allowed to leave.
+ pSubmission->submitWithInteraction( _rxHandler );
+ }
+}
+
+void Model::submit( const OUString& sID )
+{
+ submitWithInteraction( sID, nullptr );
+}
+
+css::uno::Reference<css::xforms::XDataTypeRepository> SAL_CALL Model::getDataTypeRepository( )
+{
+ if ( !mxDataTypes.is() )
+ mxDataTypes = new ODataTypeRepository;
+
+ return mxDataTypes;
+}
+
+
+// instance management
+
+
+css::uno::Reference<css::container::XSet> Model::getInstances()
+{
+ return mxInstances;
+}
+
+css::uno::Reference<css::xml::dom::XDocument> Model::getInstanceDocument( const OUString& rName )
+{
+ ensureAtLeastOneInstance();
+ Reference<XDocument> aInstance;
+ sal_Int32 nInstance = lcl_findInstance( mxInstances.get(), rName );
+ if( nInstance != -1 )
+ getInstanceData( mxInstances->getItem( nInstance ),
+ nullptr, &aInstance, nullptr, nullptr );
+ return aInstance;
+}
+
+css::uno::Reference<css::xml::dom::XDocument> SAL_CALL Model::getDefaultInstance()
+{
+ ensureAtLeastOneInstance();
+ DBG_ASSERT( mxInstances->countItems() > 0, "no instance?" );
+ Reference<XDocument> aInstance;
+ getInstanceData( mxInstances->getItem( 0 ), nullptr, &aInstance, nullptr, nullptr );
+ return aInstance;
+}
+
+
+// bindings management
+
+
+css::uno::Reference<css::beans::XPropertySet> SAL_CALL Model::createBinding()
+{
+ DBG_INVARIANT();
+ return new Binding();
+}
+
+css::uno::Reference<css::beans::XPropertySet> Model::cloneBinding( const css::uno::Reference<css::beans::XPropertySet>& xBinding )
+{
+ DBG_INVARIANT();
+ XPropertySet_t xNewBinding = createBinding();
+ copy( xBinding, xNewBinding );
+ return xNewBinding;
+}
+
+css::uno::Reference<css::beans::XPropertySet> Model::getBinding( const OUString& sId )
+{
+ DBG_INVARIANT();
+ return mxBindings->hasItem( sId ) ? mxBindings->getItem( sId ) : nullptr;
+}
+
+css::uno::Reference<css::container::XSet> Model::getBindings()
+{
+ DBG_INVARIANT();
+ return mxBindings;
+}
+
+
+// submission management
+
+
+css::uno::Reference<css::xforms::XSubmission> Model::createSubmission()
+{
+ DBG_INVARIANT();
+ return new Submission();
+}
+
+css::uno::Reference<css::xforms::XSubmission> Model::cloneSubmission(const css::uno::Reference<css::beans::XPropertySet>& xSubmission)
+{
+ DBG_INVARIANT();
+ css::uno::Reference<css::xforms::XSubmission> xNewSubmission = createSubmission();
+ XPropertySet_t xAsPropertySet( xNewSubmission );
+ copy( xSubmission, xAsPropertySet );
+ return xNewSubmission;
+}
+
+css::uno::Reference<css::xforms::XSubmission> Model::getSubmission( const OUString& sId )
+{
+ DBG_INVARIANT();
+ css::uno::Reference<css::xforms::XSubmission> xSubmission;
+ if ( mxSubmissions->hasItem( sId ) )
+ xSubmission.set(mxSubmissions->getItem( sId ), css::uno::UNO_QUERY);
+ return xSubmission;
+}
+
+css::uno::Reference<css::container::XSet> Model::getSubmissions()
+{
+ DBG_INVARIANT();
+ return mxSubmissions;
+}
+
+
+// implement XPropertySet & friends
+
+
+#define HANDLE_ID 0
+#define HANDLE_ForeignSchema 3
+#define HANDLE_SchemaRef 4
+#define HANDLE_Namespaces 5
+#define HANDLE_ExternalData 6
+
+void Model::initializePropertySet()
+{
+ registerProperty( css::beans::Property("ID", HANDLE_ID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new APIPropertyAccessor< Model, OUString >(this, &Model::setID, &Model::getID) );
+ registerProperty( css::beans::Property("ForeignSchema", HANDLE_ForeignSchema, cppu::UnoType<css::uno::Reference<css::xml::dom::XDocument>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Model, css::uno::Reference<css::xml::dom::XDocument> >( this, &Model::setForeignSchema, &Model::getForeignSchema) );
+
+ registerProperty( css::beans::Property("SchemaRef", HANDLE_SchemaRef, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Model, OUString >( this, &Model::setSchemaRef, &Model::getSchemaRef) );
+
+ registerProperty( css::beans::Property("Namespaces", HANDLE_Namespaces, cppu::UnoType<css::uno::Reference<css::container::XNameContainer>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Model, css::uno::Reference<css::container::XNameContainer> >( this, &Model::setNamespaces, &Model::getNamespaces) );
+
+ registerProperty( css::beans::Property("ExternalData", HANDLE_ExternalData, cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Model >( this, &Model::setExternalData, &Model::getExternalData ) );
+}
+
+void Model::update()
+{
+ rebuild();
+}
+
+
+sal_Int64 Model::getSomething( const css::uno::Sequence<sal_Int8>& xId )
+{
+ return comphelper::getSomethingImpl(xId, this);
+}
+
+Sequence<sal_Int8> Model::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString Model::getImplementationName()
+{
+ return "com.sun.star.form.Model";
+}
+
+sal_Bool Model::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> Model::getSupportedServiceNames()
+{
+ return {"com.sun.star.xforms.Model"};
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_form_Model_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new xforms::Model());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model.hxx b/forms/source/xforms/model.hxx
new file mode 100644
index 000000000..9deb8a834
--- /dev/null
+++ b/forms/source/xforms/model.hxx
@@ -0,0 +1,372 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include "propertysetbase.hxx"
+#include <com/sun/star/xforms/XModel2.hpp>
+#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+#include "mip.hxx"
+#include <map>
+
+
+// forward declaractions
+namespace com::sun::star
+{
+ namespace xml::dom { class XDocument; }
+ namespace xml::dom { class XNode; }
+ namespace uno { template<typename T> class Sequence; }
+ namespace lang { class IndexOutOfBoundsException; }
+ namespace lang { class IllegalArgumentException; }
+ namespace beans { class XPropertySet; }
+ namespace container { class XSet; }
+ namespace container { class XNameContainer; }
+ namespace frame { class XModel; }
+}
+namespace xforms
+{
+ class BindingCollection;
+ class SubmissionCollection;
+ class InstanceCollection;
+ class EvaluationContext;
+}
+
+
+namespace xforms
+{
+
+/** An XForms Model. Contains:
+ * # (set of) instance data (XML DOM tree)
+ * # (set of) bindings
+ * # (set of) submissions
+ * # (NOT YET IMPLEMENTED) actions (set of)
+ *
+ * See http://www.w3.org/TR/xforms/ for more information.
+ */
+typedef cppu::ImplInheritanceHelper<
+ PropertySetBase,
+ css::xforms::XModel2,
+ css::xforms::XFormsUIHelper1,
+ css::util::XUpdatable,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo
+> Model_t;
+class Model : public Model_t
+{
+ // a number of local typedefs, to make the remaining header readable
+ typedef css::uno::Reference<css::xml::dom::XNode> XNode_t;
+ typedef css::uno::Reference<css::beans::XPropertySet> XPropertySet_t;
+
+ typedef std::multimap<XNode_t,std::pair<void*,MIP> > MIPs_t;
+
+
+private:
+
+ OUString msID; /// the model ID
+ rtl::Reference<BindingCollection> mxBindings; /// the bindings
+ rtl::Reference<SubmissionCollection> mxSubmissions; /// the submissions
+ rtl::Reference<InstanceCollection> mxInstances; /// the instance(s)
+
+ css::uno::Reference<css::xforms::XDataTypeRepository> mxDataTypes; /// the XSD data-types used
+ css::uno::Reference<css::xml::dom::XDocument> mxForeignSchema; /// the XSD-schema part we cannot
+ /// map onto data types
+ OUString msSchemaRef; /// xforms:model/@schema attribute
+
+ css::uno::Reference<css::container::XNameContainer> mxNamespaces; /// namespaces for entire model
+
+ MIPs_t maMIPs; /// map nodes to their MIPs
+
+ bool mbInitialized; /// has model been initialized ?
+ bool mbExternalData; /// is the data of this model to be considered an integral part of the document?
+
+ void initializePropertySet();
+
+ void ensureAtLeastOneInstance();
+
+
+public:
+
+ /// create a new model with an empty, default instance
+ Model();
+ virtual ~Model() noexcept override;
+
+ xforms::EvaluationContext getEvaluationContext();
+
+
+ static css::uno::Sequence<sal_Int8> getUnoTunnelId();
+
+
+ // get/set that part of the schema, that we can't interpret as data types
+ css::uno::Reference<css::xml::dom::XDocument> getForeignSchema() const { return mxForeignSchema;}
+ void setForeignSchema( const css::uno::Reference<css::xml::dom::XDocument>& );
+
+ // get/set the xforms:model/@schema attribute
+ OUString getSchemaRef() const { return msSchemaRef;}
+ void setSchemaRef( const OUString& );
+
+ // get/set namespaces for entire model
+ css::uno::Reference<css::container::XNameContainer> getNamespaces() const { return mxNamespaces;}
+ void setNamespaces( const css::uno::Reference<css::container::XNameContainer>& );
+
+ // get/set the ExternalData property
+ bool getExternalData() const { return mbExternalData;}
+ void setExternalData( bool _bData );
+
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ void dbg_assertInvariant() const;
+#endif
+
+
+ // MIP (model item property) management
+
+
+ // register MIPs which apply to a given node; only to be called by bindings
+ // (The pTag parameter serves only to be able to remove the MIPs
+ // that were added using the same tag. No functions will be
+ // performed on it; hence the void* type.)
+ void addMIP( void* pTag, const XNode_t&, const MIP& );
+ void removeMIPs( void const * pTag );
+
+ /// query which MIPs apply to the given node
+ MIP queryMIP( const XNode_t& xNode ) const;
+
+ /// re-bind all bindings
+ void rebind();
+
+ /// call defer notifications on all bindings
+ void deferNotifications( bool );
+
+ /// set a data value in the instance
+ /// (also defers notifications)
+ bool setSimpleContent( const XNode_t&, const OUString& );
+
+ /// load instance data
+ void loadInstance( sal_Int32 nInstance );
+ void loadInstances();
+
+ /// has model been initialized?
+ bool isInitialized() const { return mbInitialized;}
+
+ /// is model currently valid (for submission)?
+ bool isValid() const;
+
+
+ // XModel
+ // implement the xforms::XModel implementation
+
+
+ virtual OUString SAL_CALL getID() override;
+
+ virtual void SAL_CALL setID( const OUString& sID ) override;
+
+ virtual void SAL_CALL initialize() override;
+
+ virtual void SAL_CALL rebuild() override;
+
+ virtual void SAL_CALL recalculate() override;
+
+ virtual void SAL_CALL revalidate() override;
+
+ virtual void SAL_CALL refresh() override;
+
+ virtual void SAL_CALL submit( const OUString& sID ) override;
+
+ virtual void SAL_CALL submitWithInteraction( const OUString& id, const css::uno::Reference<css::task::XInteractionHandler>& _rxHandler ) override;
+
+ virtual css::uno::Reference<css::xforms::XDataTypeRepository> SAL_CALL getDataTypeRepository( ) override;
+
+
+ // XModel: instance management
+
+ virtual css::uno::Reference<css::container::XSet> SAL_CALL getInstances() override;
+
+ virtual css::uno::Reference<css::xml::dom::XDocument> SAL_CALL getInstanceDocument( const OUString& ) override;
+
+ virtual css::uno::Reference<css::xml::dom::XDocument> SAL_CALL getDefaultInstance() override;
+
+
+ // XModel: binding management
+
+ virtual css::uno::Reference<css::beans::XPropertySet> SAL_CALL createBinding() override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet> SAL_CALL cloneBinding( const css::uno::Reference<css::beans::XPropertySet>& ) override;
+
+ virtual css::uno::Reference<css::beans::XPropertySet> SAL_CALL getBinding( const OUString& ) override;
+
+ virtual css::uno::Reference<css::container::XSet> SAL_CALL getBindings() override;
+
+
+ // XModel: submission management
+
+ virtual css::uno::Reference<css::xforms::XSubmission> SAL_CALL createSubmission() override;
+
+ virtual css::uno::Reference<css::xforms::XSubmission> SAL_CALL cloneSubmission( const css::uno::Reference<css::beans::XPropertySet>& ) override;
+
+ virtual css::uno::Reference<css::xforms::XSubmission> SAL_CALL getSubmission( const OUString& ) override;
+
+ virtual css::uno::Reference<css::container::XSet> SAL_CALL getSubmissions() override;
+
+ // XPropertySet
+
+ virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p) override
+ { return PropertySetBase::getPropertyValue(p); }
+
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { PropertySetBase::addPropertyChangeListener(p1, p2); }
+
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { PropertySetBase::removePropertyChangeListener(p1, p2); }
+
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { PropertySetBase::addVetoableChangeListener(p1, p2); }
+
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { PropertySetBase::removeVetoableChangeListener(p1, p2); }
+
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override
+ { return PropertySetBase::getPropertySetInfo(); }
+
+ virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
+ { PropertySetBase::setPropertyValue(p1, p2); }
+
+
+ // XFormsUIHelper1 & friends:
+ // (implementation in model_ui.cxx)
+
+
+ /// determine a reasonable control service for a given node
+ /// (based on data type MIP assigned to the node)
+ virtual OUString SAL_CALL getDefaultServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode ) override;
+
+ /// call getDefaultBindingExpressionForNode with default evaluation context
+ virtual OUString SAL_CALL getDefaultBindingExpressionForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode ) override;
+
+ /// determine a reasonable default binding expression for a given node
+ /// and a given evaluation context
+ /// @returns expression, or empty string if no expression could be derived
+ OUString getDefaultBindingExpressionForNode(
+ const XNode_t&,
+ const EvaluationContext& );
+
+ virtual OUString SAL_CALL getNodeDisplayName( const css::uno::Reference<css::xml::dom::XNode>&,
+ sal_Bool bDetail ) override;
+
+ virtual OUString SAL_CALL getNodeName( const css::uno::Reference<css::xml::dom::XNode>& ) override;
+
+ virtual OUString SAL_CALL getBindingName( const css::uno::Reference< ::css::beans::XPropertySet >&,
+ sal_Bool bDetail ) override;
+
+ virtual OUString SAL_CALL getSubmissionName( const css::uno::Reference< ::css::beans::XPropertySet >&,
+ sal_Bool bDetail ) override;
+
+ virtual css::uno::Reference< ::css::beans::XPropertySet > SAL_CALL cloneBindingAsGhost( const css::uno::Reference< ::css::beans::XPropertySet >& ) override;
+
+ virtual void SAL_CALL removeBindingIfUseless( const css::uno::Reference< ::css::beans::XPropertySet >& ) override;
+
+ virtual css::uno::Reference<css::xml::dom::XDocument> SAL_CALL newInstance( const OUString& sName,
+ const OUString& sURL,
+ sal_Bool bURLOnce ) override;
+
+ virtual void SAL_CALL renameInstance( const OUString& sFrom,
+ const OUString& sTo,
+ const OUString& sURL,
+ sal_Bool bURLOnce ) override;
+
+ virtual void SAL_CALL removeInstance( const OUString& sName ) override;
+
+
+ virtual css::uno::Reference<css::xforms::XModel> SAL_CALL newModel( const css::uno::Reference<css::frame::XModel>& xComponent,
+ const OUString& sName ) override;
+ virtual void SAL_CALL renameModel( const css::uno::Reference<css::frame::XModel>& xComponent,
+ const OUString& sFrom,
+ const OUString& sTo ) override;
+
+ virtual void SAL_CALL removeModel( const css::uno::Reference<css::frame::XModel>& xComponent,
+ const OUString& sName ) override;
+
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL createElement(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xParent,
+ const OUString& sName ) override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL createAttribute(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xParent,
+ const OUString& sName ) override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL renameNode(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
+ const OUString& sName ) override;
+
+ virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL getBindingForNode( const
+ css::uno::Reference<css::xml::dom::XNode>&,
+ sal_Bool bCreate ) override;
+
+ virtual void SAL_CALL removeBindingForNode( const css::uno::Reference< ::css::xml::dom::XNode >& ) override;
+
+ virtual OUString SAL_CALL getResultForExpression(
+ const css::uno::Reference< css::beans::XPropertySet >& xBinding,
+ sal_Bool bIsBindingExpression,
+ const OUString& sExpression ) override;
+
+ virtual sal_Bool SAL_CALL isValidXMLName( const OUString& sName ) override;
+
+ virtual sal_Bool SAL_CALL isValidPrefixName( const OUString& sName ) override;
+
+ virtual void SAL_CALL setNodeValue(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
+ const OUString& sValue ) override;
+
+
+ // XUpdatable
+
+
+public:
+ virtual void SAL_CALL update() override;
+
+
+ // XUnoTunnel
+
+
+public:
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence<sal_Int8>& ) override;
+
+
+ // XTypeProvider::getImplementationId
+
+
+public:
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model_helper.hxx b/forms/source/xforms/model_helper.hxx
new file mode 100644
index 000000000..d8e98c98e
--- /dev/null
+++ b/forms/source/xforms/model_helper.hxx
@@ -0,0 +1,146 @@
+/* -*- 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
+
+
+// some helper definitions that must be available for model.cxx and
+// model_ui.cxx
+
+
+#include "namedcollection.hxx"
+#include "binding.hxx"
+#include "submission.hxx"
+#include "unohelper.hxx"
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/servicehelper.hxx>
+
+namespace xforms
+{
+ class Model;
+}
+
+
+// BindingCollection
+
+
+namespace xforms
+{
+
+class BindingCollection : public NamedCollection<css::uno::Reference<css::beans::XPropertySet> >
+{
+ Model* mpModel;
+
+public:
+ explicit BindingCollection( Model* pModel ) : mpModel( pModel ) {}
+
+ virtual bool isValid( const T& t ) const override
+ {
+ return comphelper::getFromUnoTunnel<Binding>( t ) != nullptr;
+ }
+
+protected:
+ virtual void _insert( const T& t ) override
+ {
+ auto pBinding = comphelper::getFromUnoTunnel<Binding>( t );
+ OSL_ENSURE( pBinding != nullptr, "invalid item?" );
+ pBinding->_setModel( css::uno::Reference<css::xforms::XModel>( mpModel ) );
+ }
+
+ virtual void _remove( const T& t ) override
+ {
+ auto pBinding = comphelper::getFromUnoTunnel<Binding>( t );
+ OSL_ENSURE( pBinding != nullptr, "invalid item?" );
+ pBinding->_setModel( css::uno::Reference<css::xforms::XModel>() );
+ }
+};
+
+class SubmissionCollection : public NamedCollection<css::uno::Reference<css::beans::XPropertySet> >
+{
+ Model* mpModel;
+
+public:
+ explicit SubmissionCollection( Model* pModel ) : mpModel( pModel ) {}
+
+ virtual bool isValid( const T& t ) const override
+ {
+ return comphelper::getFromUnoTunnel<Submission>( t ) != nullptr;
+ }
+
+protected:
+ virtual void _insert( const T& t ) override
+ {
+ auto pSubmission = comphelper::getFromUnoTunnel<Submission>( t );
+ OSL_ENSURE( pSubmission != nullptr, "invalid item?" );
+ pSubmission->setModel( css::uno::Reference<css::xforms::XModel>( mpModel ) );
+ }
+
+ virtual void _remove( const T& t ) override
+ {
+ auto pSubmission = comphelper::getFromUnoTunnel<Submission>( t );
+ OSL_ENSURE( pSubmission != nullptr, "invalid item?" );
+ pSubmission->setModel( css::uno::Reference<css::xforms::XModel>( ) );
+ }
+};
+
+class InstanceCollection : public Collection<css::uno::Sequence<css::beans::PropertyValue> >
+{
+public:
+ virtual bool isValid( const T& t ) const override
+ {
+ for( const css::beans::PropertyValue& rProp : t )
+ {
+ if (rProp.Name == "Instance" )
+ return true;
+ }
+ return false;
+ }
+};
+
+
+// helper functions
+
+
+sal_Int32 lcl_findInstance( const InstanceCollection*,
+ std::u16string_view );
+
+
+// get values from Sequence<PropertyValue> describing an Instance
+void getInstanceData(
+ const css::uno::Sequence<css::beans::PropertyValue>&,
+ OUString* pID,
+ css::uno::Reference<css::xml::dom::XDocument>*,
+ OUString* pURL,
+ bool* pURLOnce );
+
+// set values on Sequence<PropertyValue> for an Instance
+void setInstanceData(
+ css::uno::Sequence<css::beans::PropertyValue>&,
+ const OUString* pID,
+ const css::uno::Reference<css::xml::dom::XDocument>*,
+ const OUString* pURL,
+ const bool* pURLOnce );
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/model_ui.cxx b/forms/source/xforms/model_ui.cxx
new file mode 100644
index 000000000..616ea8863
--- /dev/null
+++ b/forms/source/xforms/model_ui.cxx
@@ -0,0 +1,1007 @@
+/* -*- 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 "model.hxx"
+#include "model_helper.hxx"
+#include "mip.hxx"
+#include "evaluationcontext.hxx"
+#include "unohelper.hxx"
+#include "submission/serialization_app_xml.hxx"
+#include "resourcehelper.hxx"
+#include "xmlhelper.hxx"
+#include "convert.hxx"
+#include <strings.hrc>
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/processfactory.hxx>
+
+// UNO classes
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/dom/XNamedNodeMap.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <com/sun/star/xml/xpath/XPathObjectType.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/io/TextInputStream.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/xforms/XDataTypeRepository.hpp>
+#include <com/sun/star/xsd/XDataType.hpp>
+#include <com/sun/star/xsd/DataTypeClass.hpp>
+
+
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::io::TextInputStream;
+using com::sun::star::io::XTextInputStream2;
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::xforms::XFormsSupplier;
+
+using namespace xforms;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::xml::dom;
+using namespace com::sun::star::xml::xpath;
+
+
+// implement XFormsUIHelper1
+
+
+OUString Model::getDefaultServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode )
+{
+ // determine service for control. string/text field is default.
+ OUString sService = "com.sun.star.form.component.TextField";
+
+ // query repository for suitable type
+ OSL_ENSURE( mxDataTypes.is(), "no type repository?" );
+ OUString sTypeName = queryMIP( xNode ).getTypeName();
+ if( mxDataTypes->hasByName( sTypeName ) )
+ {
+ OSL_ENSURE( mxDataTypes->getDataType( sTypeName ).is(),
+ "has or has not?" );
+
+ switch( mxDataTypes->getDataType( sTypeName )->getTypeClass() )
+ {
+ case css::xsd::DataTypeClass::BOOLEAN:
+ sService = "com.sun.star.form.component.CheckBox";
+ break;
+ case css::xsd::DataTypeClass::DOUBLE:
+ case css::xsd::DataTypeClass::DECIMAL:
+ case css::xsd::DataTypeClass::FLOAT:
+ sService = "com.sun.star.form.component.NumericField";
+ break;
+
+ case css::xsd::DataTypeClass::STRING:
+ case css::xsd::DataTypeClass::DURATION:
+ case css::xsd::DataTypeClass::DATETIME:
+ case css::xsd::DataTypeClass::TIME:
+ case css::xsd::DataTypeClass::DATE:
+ case css::xsd::DataTypeClass::gYearMonth:
+ case css::xsd::DataTypeClass::gYear:
+ case css::xsd::DataTypeClass::gMonthDay:
+ case css::xsd::DataTypeClass::gDay:
+ case css::xsd::DataTypeClass::gMonth:
+ case css::xsd::DataTypeClass::hexBinary:
+ case css::xsd::DataTypeClass::base64Binary:
+ case css::xsd::DataTypeClass::anyURI:
+ case css::xsd::DataTypeClass::QName:
+ case css::xsd::DataTypeClass::NOTATION:
+ default:
+ // keep default
+ break;
+ }
+ }
+
+ return sService;
+}
+
+
+static void lcl_OutPosition( OUStringBuffer& rBuffer,
+ const Reference<XNode>& xNode )
+{
+ OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
+
+ // count # of occurrences of this node
+ sal_Int32 nFound = 0;
+ sal_Int32 nPosition = -1;
+ if( xNode->getParentNode().is() )
+ {
+ for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
+ xIter != nullptr;
+ xIter = xIter->getNextSibling() )
+ {
+ if( xIter->getNodeType() == xNode->getNodeType() &&
+ xIter->getNodeName() == xNode->getNodeName() &&
+ xIter->getNamespaceURI() == xNode->getNamespaceURI() )
+ {
+ nFound++;
+ if( xIter == xNode )
+ nPosition = nFound;
+ }
+ }
+ }
+ OSL_ENSURE( nFound > 0 && nPosition > 0, "node not found???" );
+
+ // output position (if necessary)
+ if( nFound > 1 )
+ {
+ rBuffer.insert( 0, ']' );
+ rBuffer.insert( 0, nPosition );
+ rBuffer.insert( 0, '[' );
+ }
+}
+
+static void lcl_OutName( OUStringBuffer& rBuffer,
+ const Reference<XNode>& xNode )
+{
+ rBuffer.insert( 0, xNode->getNodeName() );
+ OUString sPrefix = xNode->getPrefix();
+ if( !sPrefix.isEmpty() )
+ {
+ rBuffer.insert( 0, ':' );
+ rBuffer.insert( 0, sPrefix );
+ }
+}
+
+static void lcl_OutInstance( OUStringBuffer& rBuffer,
+ const Reference<XNode>& xNode,
+ Model* pModel )
+{
+ Reference<XDocument> xDoc = xNode->getOwnerDocument();
+
+ if( xDoc == pModel->getDefaultInstance() )
+ return;
+
+ rBuffer.insert( 0, "')" );
+
+ // iterate over instances, and find the right one
+ OUString sInstanceName;
+ Reference<XEnumeration> xEnum =
+ pModel->getInstances()->createEnumeration();
+ while( sInstanceName.isEmpty() && xEnum->hasMoreElements() )
+ {
+ Sequence<PropertyValue> aValues;
+ xEnum->nextElement() >>= aValues;
+
+ // get ID and instance
+ OUString sId;
+ Reference<XDocument> xInstance;
+ getInstanceData( aValues, &sId, &xInstance, nullptr, nullptr );
+
+ // now check whether this was our instance:
+ if( xInstance == xDoc )
+ sInstanceName = sId;
+ }
+
+ rBuffer.insert( 0, sInstanceName );
+ rBuffer.insert( 0, "instance('" );
+}
+
+OUString Model::getDefaultBindingExpressionForNode(
+ const XNode_t& xNode,
+ const EvaluationContext& rContext)
+{
+ OSL_ENSURE( xNode.is(), "need node" );
+
+ // iterate upwards and put sections into the expression buffer.
+ // Stop iteration either at context node (relative expression) or
+ // at document root, whichever occurs first.
+ OUStringBuffer aBuffer;
+ for( Reference<XNode> xCurrent = xNode;
+ xCurrent.is() && xCurrent != rContext.mxContextNode;
+ xCurrent = xCurrent->getParentNode() )
+ {
+ // insert a '/' for every step except the first
+ if( !aBuffer.isEmpty() )
+ aBuffer.insert( 0, '/' );
+
+ switch( xCurrent->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ lcl_OutPosition( aBuffer, xCurrent );
+ lcl_OutName( aBuffer, xCurrent );
+ break;
+
+ case NodeType_TEXT_NODE:
+ lcl_OutPosition( aBuffer, xCurrent );
+ aBuffer.insert( 0, "text()" );
+ break;
+
+ case NodeType_ATTRIBUTE_NODE:
+ lcl_OutName( aBuffer, xCurrent );
+ aBuffer.insert( 0, '@' );
+ break;
+
+ case NodeType_DOCUMENT_NODE:
+ // check for which instance we have
+ lcl_OutInstance( aBuffer, xCurrent, this );
+ break;
+
+ default:
+ // unknown type? fail!
+ OSL_FAIL( "unknown node type!" );
+ return OUString();
+ }
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+
+OUString Model::getDefaultBindingExpressionForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode )
+{
+ return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
+}
+
+static bool lcl_isWhitespace( const OUString& rString )
+{
+ sal_Int32 nLength = rString.getLength();
+ const sal_Unicode* pStr = rString.getStr();
+
+ bool bWhitespace = true;
+ for( sal_Int32 i = 0; bWhitespace && ( i < nLength ); i++ )
+ {
+ sal_Unicode c = pStr[i];
+ bWhitespace = ( c == u'\x0009' ||
+ c == u'\x000A' ||
+ c == u'\x000D' ||
+ c == u' ' );
+ }
+ return bWhitespace;
+}
+
+OUString Model::getNodeDisplayName( const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ sal_Bool bDetail )
+{
+ OUStringBuffer aBuffer;
+
+ switch( xNode->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ lcl_OutName( aBuffer, xNode );
+ break;
+
+ case NodeType_TEXT_NODE:
+ {
+ OUString sContent = xNode->getNodeValue();
+ if( bDetail || ! lcl_isWhitespace( sContent ) )
+ {
+ aBuffer.append("\"" + Convert::collapseWhitespace( sContent ) + "\"");
+ }
+ }
+ break;
+
+ case NodeType_ATTRIBUTE_NODE:
+ lcl_OutName( aBuffer, xNode );
+ aBuffer.insert( 0, '@' );
+ break;
+
+ case NodeType_DOCUMENT_NODE:
+ if( xNode == getDefaultInstance() )
+ aBuffer.append( '/' );
+ else
+ lcl_OutInstance( aBuffer, xNode, this );
+ break;
+
+ default:
+ // unknown type? fail!
+ OSL_FAIL( "unknown node type!" );
+ break;
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString Model::getNodeName( const css::uno::Reference<css::xml::dom::XNode>& xNode )
+{
+ OUStringBuffer aBuffer;
+
+ switch( xNode->getNodeType() )
+ {
+ case NodeType_ELEMENT_NODE:
+ case NodeType_ATTRIBUTE_NODE:
+ lcl_OutName( aBuffer, xNode );
+ break;
+
+ case NodeType_TEXT_NODE:
+ case NodeType_DOCUMENT_NODE:
+ default:
+ // unknown type? fail!
+ OSL_FAIL( "no name for this node type!" );
+ break;
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString Model::getBindingName( const css::uno::Reference< ::css::beans::XPropertySet >& xBinding,
+ sal_Bool /*bDetail*/ )
+{
+ OUString sID;
+ xBinding->getPropertyValue( "BindingID" ) >>= sID;
+ OUString sExpression;
+ xBinding->getPropertyValue( "BindingExpression" ) >>= sExpression;
+
+ OUString sRet;
+ if( !sID.isEmpty() )
+ {
+ sRet = sID + " (" + sExpression + ") ";
+ }
+ else
+ sRet = sExpression;
+
+ return sRet;
+}
+
+OUString Model::getSubmissionName( const css::uno::Reference< ::css::beans::XPropertySet >& xSubmission,
+ sal_Bool /*bDetail*/ )
+{
+ OUString sID;
+ xSubmission->getPropertyValue( "ID" ) >>= sID;
+ return sID;
+}
+
+css::uno::Reference< ::css::beans::XPropertySet > Model::cloneBindingAsGhost( const css::uno::Reference< ::css::beans::XPropertySet > &xBinding )
+{
+ // Create a new binding instance first...
+ rtl::Reference<Binding> pBinding = new Binding();
+
+ // ...and bump up the "deferred notification counter"
+ // to prevent this binding from contributing to the
+ // MIPs table...
+ pBinding->deferNotifications(true);
+
+ // Copy the propertyset and return result...
+ XPropertySet_t xNewBinding(pBinding);
+ copy( xBinding, xNewBinding );
+ return xNewBinding;
+}
+
+void Model::removeBindingIfUseless( const css::uno::Reference< ::css::beans::XPropertySet >& xBinding )
+{
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( xBinding );
+ if( pBinding != nullptr )
+ {
+ if( ! pBinding->isUseful() )
+ mxBindings->removeItem( pBinding );
+ }
+}
+
+css::uno::Reference<css::xml::dom::XDocument> Model::newInstance( const OUString& sName,
+ const OUString& sURL,
+ sal_Bool bURLOnce )
+{
+ // create a default instance with <instanceData> element
+ css::uno::Reference<css::xml::dom::XDocument> xInstance = getDocumentBuilder()->newDocument();
+ DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
+
+ Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
+ Reference<XNode>( xInstance->createElement( "instanceData" ),
+ UNO_QUERY_THROW ) );
+
+ Sequence<PropertyValue> aSequence;
+ bool bOnce = bURLOnce; // bool, so we can take address in setInstanceData
+ setInstanceData( aSequence, &sName, &xInstance, &sURL, &bOnce );
+ sal_Int32 nInstance = mxInstances->addItem( aSequence );
+ loadInstance( nInstance );
+
+ return xInstance;
+}
+
+static sal_Int32 lcl_findProp( const PropertyValue* pValues,
+ sal_Int32 nLength,
+ std::u16string_view rName )
+{
+ bool bFound = false;
+ sal_Int32 n = 0;
+ for( ; !bFound && n < nLength; n++ )
+ {
+ bFound = ( pValues[n].Name == rName );
+ }
+ return bFound ? ( n - 1) : -1;
+}
+
+sal_Int32 xforms::lcl_findInstance( const InstanceCollection* pInstances,
+ std::u16string_view rName )
+{
+ sal_Int32 nLength = pInstances->countItems();
+ sal_Int32 n = 0;
+ bool bFound = false;
+ for( ; !bFound && n < nLength; n++ )
+ {
+ OUString sName;
+ getInstanceData( pInstances->getItem( n ), &sName, nullptr, nullptr, nullptr );
+ bFound = ( sName == rName );
+ }
+ return bFound ? ( n - 1 ) : -1;
+}
+
+void Model::renameInstance( const OUString& sFrom,
+ const OUString& sTo,
+ const OUString& sURL,
+ sal_Bool bURLOnce )
+{
+ sal_Int32 nPos = lcl_findInstance( mxInstances.get(), sFrom );
+ if( nPos == -1 )
+ return;
+
+ Sequence<PropertyValue> aSeq = mxInstances->getItem( nPos );
+ PropertyValue* pSeq = aSeq.getArray();
+ sal_Int32 nLength = aSeq.getLength();
+
+ sal_Int32 nProp = lcl_findProp( pSeq, nLength, u"ID" );
+ if( nProp == -1 )
+ {
+ // add name property
+ aSeq.realloc( nLength + 1 );
+ pSeq = aSeq.getArray();
+ pSeq[ nLength ].Name = "ID";
+ nProp = nLength;
+ }
+
+ // change name
+ pSeq[ nProp ].Value <<= sTo;
+
+ // change url
+ nProp = lcl_findProp( pSeq, nLength, u"URL" );
+ if(nProp != -1)
+ pSeq[ nProp ].Value <<= sURL;
+
+ // change urlonce
+ nProp = lcl_findProp( pSeq, nLength, u"URLOnce" );
+ if(nProp != -1)
+ pSeq[ nProp ].Value <<= bURLOnce;
+
+ // set instance
+ mxInstances->setItem( nPos, aSeq );
+}
+
+void Model::removeInstance( const OUString& sName )
+{
+ sal_Int32 nPos = lcl_findInstance( mxInstances.get(), sName );
+ if( nPos != -1 )
+ mxInstances->removeItem( mxInstances->getItem( nPos ) );
+}
+
+static Reference<XNameContainer> lcl_getModels(
+ const Reference<css::frame::XModel>& xComponent )
+{
+ Reference<XNameContainer> xRet;
+ Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
+ if( xSupplier.is() )
+ {
+ xRet = xSupplier->getXForms();
+ }
+ return xRet;
+}
+
+css::uno::Reference<css::xforms::XModel> Model::newModel( const Reference<css::frame::XModel>& xCmp,
+ const OUString& sName )
+{
+ css::uno::Reference<css::xforms::XModel> xModel;
+ Reference<XNameContainer> xModels = lcl_getModels( xCmp );
+ if( xModels.is()
+ && ! xModels->hasByName( sName ) )
+ {
+ rtl::Reference<Model> pModel = new Model();
+ xModel.set( pModel );
+
+ pModel->setID( sName );
+ pModel->newInstance( OUString(), OUString(), false );
+ pModel->initialize();
+ xModels->insertByName( sName, Any( xModel ) );
+ }
+
+ return xModel;
+}
+
+void Model::renameModel( const Reference<css::frame::XModel>& xCmp,
+ const OUString& sFrom,
+ const OUString& sTo )
+{
+ Reference<XNameContainer> xModels = lcl_getModels( xCmp );
+ if( xModels.is()
+ && xModels->hasByName( sFrom )
+ && ! xModels->hasByName( sTo ) )
+ {
+ Reference<XModel> xModel( xModels->getByName( sFrom ), UNO_QUERY );
+ xModel->setID( sTo );
+ xModels->insertByName( sTo, Any( xModel ) );
+ xModels->removeByName( sFrom );
+ }
+}
+
+void Model::removeModel( const Reference<css::frame::XModel>& xCmp,
+ const OUString& sName )
+{
+ Reference<XNameContainer> xModels = lcl_getModels( xCmp );
+ if( xModels.is()
+ && xModels->hasByName( sName ) )
+ {
+ xModels->removeByName( sName );
+ }
+}
+
+css::uno::Reference<css::xml::dom::XNode> Model::createElement( const css::uno::Reference<css::xml::dom::XNode>& xParent,
+ const OUString& sName )
+{
+ Reference<XNode> xNode;
+ if( xParent.is()
+ && isValidXMLName( sName ) )
+ {
+ // TODO: implement proper namespace handling
+ xNode = xParent->getOwnerDocument()->createElement( sName );
+ }
+ return xNode;
+}
+
+css::uno::Reference<css::xml::dom::XNode> Model::createAttribute( const css::uno::Reference<css::xml::dom::XNode>& xParent,
+ const OUString& sName )
+{
+ Reference<XNode> xNode;
+ Reference<XElement> xElement( xParent, UNO_QUERY );
+ if( xParent.is()
+ && xElement.is()
+ && isValidXMLName( sName ) )
+ {
+ // handle case where attribute already exists
+ sal_Int32 nCount = 0;
+ OUString sUniqueName = sName;
+ while( xElement->hasAttribute( sUniqueName ) )
+ {
+ nCount++;
+ sUniqueName = sName + OUString::number( nCount );
+ }
+
+ // TODO: implement proper namespace handling
+ xNode = xParent->getOwnerDocument()->createAttribute( sUniqueName );
+ }
+ return xNode;
+}
+
+css::uno::Reference<css::xml::dom::XNode> Model::renameNode( const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ const OUString& sName )
+{
+ // early out if we don't have to change the name
+ if( xNode->getNodeName() == sName )
+ return xNode;
+
+ // refuse to change name if it's an attribute, and the name is already used
+ if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE
+ && xNode->getParentNode().is()
+ && Reference<XElement>(xNode->getParentNode(), UNO_QUERY_THROW)->hasAttribute( sName ) )
+ return xNode;
+
+ // note old binding expression so we can adjust bindings below
+ OUString sOldDefaultBindingExpression =
+ getDefaultBindingExpressionForNode( xNode );
+
+ Reference<XDocument> xDoc = xNode->getOwnerDocument();
+ Reference<XNode> xNew;
+ if( xNode->getNodeType() == NodeType_ELEMENT_NODE )
+ {
+ Reference<XElement> xElem = xDoc->createElement( sName );
+ xNew = xElem;
+
+ // iterate over all attributes and append them to the new element
+ Reference<XElement> xOldElem( xNode, UNO_QUERY );
+ OSL_ENSURE( xNode.is(), "no element?" );
+
+ Reference<XNamedNodeMap> xMap = xNode->getAttributes();
+ sal_Int32 nLength = xMap.is() ? xMap->getLength() : 0;
+ // looping until nLength is suspicious wrt removeAttributeNode
+ // presumably shrinking XNamedNodeMap::getLength by 1
+ for( sal_Int32 n = 0; n < nLength; n++ )
+ {
+ Reference<XAttr> xAttr( xMap->item(n), UNO_QUERY );
+ xElem->setAttributeNode( xOldElem->removeAttributeNode( xAttr ) );
+ }
+
+ // iterate over all children and append them to the new element
+ for( Reference<XNode> xCurrent = xNode->getFirstChild();
+ xCurrent.is();
+ xCurrent = xNode->getFirstChild() )
+ {
+ xNew->appendChild( xNode->removeChild( xCurrent ) );
+ }
+
+ xNode->getParentNode()->replaceChild( xNew, xNode );
+ }
+ else if( xNode->getNodeType() == NodeType_ATTRIBUTE_NODE )
+ {
+ // create new attribute
+ Reference<XAttr> xAttr = xDoc->createAttribute( sName );
+ xAttr->setValue( xNode->getNodeValue() );
+
+ // replace node
+ Reference<XNode> xParent = xNode->getParentNode();
+ xParent->removeChild( xNode );
+ xNew = xParent->appendChild( xAttr );
+ }
+ else
+ {
+ OSL_FAIL( "can't rename this node type" );
+ }
+
+ // adjust bindings (if necessary):
+ if( xNew.is() )
+ {
+ // iterate over bindings and replace default expressions
+ OUString sNewDefaultBindingExpression =
+ getDefaultBindingExpressionForNode( xNew );
+ for( sal_Int32 n = 0; n < mxBindings->countItems(); n++ )
+ {
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>(
+ mxBindings->Collection<XPropertySet_t>::getItem( n ) );
+
+ if( pBinding->getBindingExpression()
+ == sOldDefaultBindingExpression )
+ pBinding->setBindingExpression( sNewDefaultBindingExpression );
+ }
+ }
+
+ // return node; return old node if renaming failed
+ return xNew.is() ? xNew : xNode;
+}
+
+css::uno::Reference< ::css::beans::XPropertySet > Model::getBindingForNode( const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ sal_Bool bCreate )
+{
+ OSL_ENSURE( xNode.is(), "no node?" );
+
+ // We will iterate over all bindings and determine the
+ // appropriateness of the respective binding for this node. The
+ // best one will be used. If we don't find any and bCreate is set,
+ // then we will create a suitable binding.
+ rtl::Reference<Binding> pBestBinding;
+ sal_Int32 nBestScore = 0;
+
+ for( sal_Int32 n = 0; n < mxBindings->countItems(); n++ )
+ {
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>(
+ mxBindings->Collection<XPropertySet_t>::getItem( n ) );
+
+ OSL_ENSURE( pBinding != nullptr, "no binding?" );
+ Reference<XNodeList> xNodeList = pBinding->getXNodeList();
+
+ sal_Int32 nNodes = xNodeList.is() ? xNodeList->getLength() : 0;
+ if( nNodes > 0 && xNodeList->item( 0 ) == xNode )
+ {
+ // allright, we found a suitable node. Let's determine how
+ // well it fits. Score:
+ // - bind to exactly this node is better than whole nodeset
+ // - simple binding expressions is better than complex ones
+ sal_Int32 nScore = 0;
+ if( nNodes == 1 )
+ nScore ++;
+ if( pBinding->isSimpleBindingExpression() )
+ nScore ++;
+
+ // if we found a better binding, remember it
+ if( nScore > nBestScore )
+ {
+ pBestBinding = pBinding;
+ nBestScore = nScore;
+ }
+ }
+ }
+
+ // create binding, if none was found and bCreate is set
+ OSL_ENSURE( ( nBestScore == 0 ) == ( pBestBinding == nullptr ),
+ "score != binding?" );
+ if( bCreate && pBestBinding == nullptr )
+ {
+ pBestBinding = new Binding();
+ pBestBinding->setBindingExpression(
+ getDefaultBindingExpressionForNode( xNode ) );
+ mxBindings->addItem( pBestBinding );
+ }
+
+ return pBestBinding;
+}
+
+void Model::removeBindingForNode( const css::uno::Reference<css::xml::dom::XNode>& )
+{
+ // determine whether suitable binding is still used
+}
+
+static OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
+{
+ OUString sResult;
+ OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
+ if ( _rxAttrNode.is() )
+ {
+ OUString sValue = _rxAttrNode->getValue();
+ sal_Unicode nQuote = '"';
+ if ( sValue.indexOf( nQuote ) >= 0 )
+ nQuote = '\'';
+
+ sResult = _rxAttrNode->getName() + "=" + OUStringChar(nQuote) + sValue + OUStringChar(nQuote) + " ";
+ }
+ return sResult;
+}
+
+static OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
+{
+ OUStringBuffer sResult;
+
+ // create document fragment
+ Reference<XDocument> xDocument( getDocumentBuilder()->newDocument() );
+ Reference<XDocumentFragment> xFragment(
+ xDocument->createDocumentFragment() );
+ OSL_ENSURE( xFragment.is(), "xFragment" );
+
+ sal_Int32 nAttributeNodes = 0;
+
+ // attach nodelist to fragment
+ sal_Int32 nLength = xNodes->getLength();
+ for( sal_Int32 i = 0; i < nLength; i++ )
+ {
+ Reference<XNode> xCurrent = xNodes->item( i );
+
+ switch ( xCurrent->getNodeType() )
+ {
+ case NodeType_DOCUMENT_NODE:
+ // special-case documents: use top-level element instead
+ xCurrent = xCurrent->getFirstChild();
+ break;
+ case NodeType_ATTRIBUTE_NODE:
+ {
+ Reference< XAttr > xAttr( xCurrent, UNO_QUERY );
+ if ( xAttr.is() )
+ {
+ sResult.append(lcl_serializeForDisplay( xAttr ));
+ ++nAttributeNodes;
+ }
+ }
+ continue;
+
+ default:
+ break;
+ }
+
+ // append node
+ xFragment->appendChild( xDocument->importNode( xCurrent, true ) );
+ }
+ OSL_ENSURE( ( nAttributeNodes == 0 ) || ( nAttributeNodes == nLength ),
+ "lcl_serializeForDisplay: mixed attribute and non-attribute nodes?" );
+ if ( nAttributeNodes )
+ // had only attribute nodes
+ return sResult.makeStringAndClear();
+
+ // serialize fragment
+ CSerializationAppXML aSerialization;
+ aSerialization.setSource( xFragment );
+ aSerialization.serialize();
+
+ // copy stream into buffer
+ Reference<XTextInputStream2> xTextInputStream = TextInputStream::create( comphelper::getProcessComponentContext() );
+ xTextInputStream->setInputStream( aSerialization.getInputStream() );
+
+ /* WORK AROUND for problem in serialization: currently, multiple
+ XML declarations (<?xml...?>) are being written out and we don't
+ want them. When this is fixed, the code below is nice and
+ simple. The current code filters out the declarations.
+ OUString sResult = xTextInputStream->readString( Sequence<sal_Unicode>(),
+ sal_True );
+ */
+
+ // well, the serialization prepends XML header(s) that we need to
+ // remove first.
+ sResult.setLength(0);
+ while( ! xTextInputStream->isEOF() )
+ {
+ OUString sLine = xTextInputStream->readLine();
+ if( !sLine.isEmpty()
+ && !sLine.startsWith( "<?xml" ) )
+ {
+ sResult.append( sLine );
+ sResult.append( '\n' );
+ }
+ }
+
+ return sResult.makeStringAndClear();
+}
+
+static OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
+{
+ // error handling first
+ if( ! xResult.is() )
+ return getResource( RID_STR_XFORMS_CANT_EVALUATE );
+
+ // TODO: localize
+ switch( xResult->getObjectType() )
+ {
+ case XPathObjectType_XPATH_BOOLEAN:
+ return OUString::boolean(xResult->getBoolean());
+
+ case XPathObjectType_XPATH_STRING:
+ return "\"" + xResult->getString() + "\"";
+
+ case XPathObjectType_XPATH_NODESET:
+ return lcl_serializeForDisplay( xResult->getNodeList() );
+
+ case XPathObjectType_XPATH_NUMBER:
+ return OUString::number(xResult->getDouble());
+
+ case XPathObjectType_XPATH_UNDEFINED:
+ case XPathObjectType_XPATH_POINT:
+ case XPathObjectType_XPATH_RANGE:
+ case XPathObjectType_XPATH_LOCATIONSET:
+ case XPathObjectType_XPATH_USERS:
+ case XPathObjectType_XPATH_XSLT_TREE:
+ default:
+ // TODO: localized error message?
+ return OUString();
+ }
+}
+
+OUString Model::getResultForExpression(
+ const css::uno::Reference< ::css::beans::XPropertySet >& xBinding,
+ sal_Bool bIsBindingExpression,
+ const OUString& sExpression )
+{
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( xBinding );
+ if( pBinding == nullptr )
+ throw RuntimeException();
+
+ // prepare & evaluate expression
+ OUStringBuffer aBuffer;
+ ComputedExpression aExpression;
+ aExpression.setExpression( sExpression );
+ if( bIsBindingExpression )
+ {
+ // binding: use binding context and evaluation
+ aExpression.evaluate( pBinding->getEvaluationContext() );
+ aBuffer.append( lcl_serializeForDisplay( aExpression.getXPath() ) );
+ }
+ else
+ {
+ // MIP (not binding): iterate over bindings contexts
+ std::vector<EvaluationContext> aContext =
+ pBinding->getMIPEvaluationContexts();
+ for (auto const& elem : aContext)
+ {
+ aExpression.evaluate(elem);
+ aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
+ aBuffer.append( '\n' );
+ }
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+sal_Bool Model::isValidXMLName( const OUString& sName )
+{
+ return isValidQName( sName, nullptr );
+}
+
+sal_Bool Model::isValidPrefixName( const OUString& sName )
+{
+ return ::isValidPrefixName( sName, nullptr );
+}
+
+void Model::setNodeValue(
+ const css::uno::Reference< ::css::xml::dom::XNode >& xNode,
+ const OUString& sValue )
+{
+ setSimpleContent( xNode, sValue );
+}
+
+
+// helper functions from model_helper.hxx
+
+
+void xforms::getInstanceData(
+ const Sequence<PropertyValue>& aValues,
+ OUString* pID,
+ Reference<XDocument>* pInstance,
+ OUString* pURL,
+ bool* pURLOnce )
+{
+ sal_Int32 nValues = aValues.getLength();
+ const PropertyValue* pValues = aValues.getConstArray();
+ for( sal_Int32 n = 0; n < nValues; n++ )
+ {
+ const PropertyValue& rValue = pValues[n];
+ if( pID != nullptr && rValue.Name == "ID")
+ rValue.Value >>= *pID;
+ if( pInstance != nullptr && rValue.Name == "Instance")
+ rValue.Value >>= *pInstance;
+ if( pURL != nullptr && rValue.Name == "URL")
+ rValue.Value >>= *pURL;
+ if( pURLOnce != nullptr && rValue.Name == "URLOnce")
+ rValue.Value >>= *pURLOnce;
+ }
+}
+
+void xforms::setInstanceData(
+ Sequence<PropertyValue>& aSequence,
+ const OUString* _pID,
+ const Reference<XDocument>* _pInstance,
+ const OUString* _pURL,
+ const bool* _pURLOnce )
+{
+ // get old instance data
+ OUString sID;
+ Reference<XDocument> xInstance;
+ OUString sURL;
+ bool bURLOnce = false;
+ getInstanceData( aSequence, &sID, &xInstance, &sURL, &bURLOnce );
+ const OUString* pID = !sID.isEmpty() ? &sID : nullptr;
+ const Reference<XDocument>* pInstance = xInstance.is() ? &xInstance : nullptr;
+ const OUString* pURL = !sURL.isEmpty() ? &sURL : nullptr;
+ const bool* pURLOnce = ( bURLOnce && pURL != nullptr ) ? &bURLOnce : nullptr;
+
+ // determine new instance data
+ if (_pID != nullptr)
+ pID = _pID;
+ if (_pInstance != nullptr)
+ pInstance = _pInstance;
+ if (_pURL != nullptr)
+ pURL = _pURL;
+ if (_pURLOnce != nullptr)
+ pURLOnce = _pURLOnce;
+
+ // count # of values we want to set
+ sal_Int32 nCount = 0;
+ if (pID != nullptr)
+ ++nCount;
+ if (pInstance != nullptr)
+ ++nCount;
+ if (pURL != nullptr)
+ ++nCount;
+ if (pURLOnce != nullptr)
+ ++nCount;
+
+ // realloc sequence and enter values;
+ aSequence.realloc( nCount );
+ PropertyValue* pSequence = aSequence.getArray();
+ sal_Int32 nIndex = 0;
+ if(pID != nullptr)
+ {
+ pSequence[ nIndex ].Name = "ID";
+ pSequence[ nIndex ].Value <<= *pID;
+ nIndex++;
+ }
+ if(pInstance != nullptr)
+ {
+ pSequence[ nIndex ].Name = "Instance";
+ pSequence[ nIndex ].Value <<= *pInstance;
+ nIndex++;
+ }
+ if(pURL != nullptr)
+ {
+ pSequence[ nIndex ].Name = "URL";
+ pSequence[ nIndex ].Value <<= *pURL;
+ nIndex++;
+ }
+ if(pURLOnce != nullptr)
+ {
+ pSequence[ nIndex ].Name = "URLOnce";
+ pSequence[ nIndex ].Value <<= *pURLOnce;
+ nIndex++;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/namedcollection.hxx b/forms/source/xforms/namedcollection.hxx
new file mode 100644
index 000000000..4cbd99eb4
--- /dev/null
+++ b/forms/source/xforms/namedcollection.hxx
@@ -0,0 +1,111 @@
+/* -*- 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 "collection.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+
+#include <algorithm>
+
+template<class T>
+class NamedCollection : public cppu::ImplInheritanceHelper<
+ Collection<T>,
+ css::container::XNameAccess>
+{
+ using Collection<T>::maItems;
+ using Collection<T>::getItem;
+ using Collection<T>::hasItem;
+
+public:
+
+ const T& getItem( const OUString& rName ) const
+ {
+ OSL_ENSURE( hasItem( rName ), "invalid name" );
+ return *findItem( rName );
+ }
+
+ bool hasItem( const OUString& rName ) const
+ {
+ return findItem( rName ) != maItems.end();
+ }
+
+ css::uno::Sequence<OUString> getNames() const
+ {
+ // iterate over members, and collect all those that have names
+ std::vector<OUString> aNames;
+ for( const T& rItem : maItems )
+ {
+ css::uno::Reference<css::container::XNamed>
+ xNamed( rItem, css::uno::UNO_QUERY );
+ if( xNamed.is() )
+ aNames.push_back( xNamed->getName() );
+ }
+
+ return comphelper::containerToSequence(aNames);
+ }
+
+protected:
+ typename std::vector<T>::const_iterator findItem( const OUString& rName ) const
+ {
+ return std::find_if(maItems.begin(), maItems.end(), [&rName](const T& rItem) {
+ css::uno::Reference<css::container::XNamed>
+ xNamed( rItem, css::uno::UNO_QUERY );
+ return xNamed.is() && xNamed->getName() == rName;
+ });
+ }
+
+public:
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return Collection<T>::getElementType();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return Collection<T>::hasElements();
+ }
+
+ // XNameAccess : XElementAccess
+ virtual css::uno::Any SAL_CALL getByName(
+ const OUString& aName ) override
+ {
+ if( !hasItem( aName ) )
+ throw css::container::NoSuchElementException();
+ return css::uno::Any( getItem( aName ) );
+ }
+
+ virtual css::uno::Sequence<OUString> SAL_CALL getElementNames() override
+ {
+ return getNames();
+ }
+
+ virtual sal_Bool SAL_CALL hasByName(
+ const OUString& aName ) override
+ {
+ return hasItem( aName );
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/pathexpression.cxx b/forms/source/xforms/pathexpression.cxx
new file mode 100644
index 000000000..c26d13d5a
--- /dev/null
+++ b/forms/source/xforms/pathexpression.cxx
@@ -0,0 +1,113 @@
+/* -*- 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 "pathexpression.hxx"
+#include "unohelper.hxx"
+#include "evaluationcontext.hxx"
+
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+
+using com::sun::star::uno::Reference;
+using com::sun::star::xml::dom::XNode;
+using com::sun::star::xml::dom::XNodeList;
+
+
+namespace xforms
+{
+
+PathExpression::PathExpression()
+{
+}
+
+PathExpression::~PathExpression()
+{
+}
+
+
+void PathExpression::setExpression( const OUString& rExpression )
+{
+ // set new expression, and clear pre-computed results
+ ComputedExpression::setExpression( rExpression );
+
+ // check expression against regular expression to determine
+ // whether it contains only 'simple' (i.e. static) conditions. For
+ // now, we check whether it only contains number positions.
+ // (TODO: Only works for names containing only ASCII letters+digits.)
+ mbIsSimple =
+ _checkExpression( "( */@?[a-zA-Z0-9:]+( *\\[ *[0-9 ]+ *\\] *)?)+" );
+
+ maNodes.clear();
+}
+
+OUString PathExpression::_getExpressionForEvaluation() const
+{
+ OUString sExpr = ComputedExpression::_getExpressionForEvaluation();
+ if( sExpr.isEmpty())
+ sExpr = ".";
+ return sExpr;
+}
+
+void PathExpression::evaluate( const EvaluationContext& rContext )
+{
+ // for simple expression we don't need to re-bind (if we were bound before)
+ // (we will evaluate empty expressions, since they are interpreted as ".")
+ if( mxResult.is() && isSimpleExpression() )
+ return;
+
+ _evaluate( rContext, _getExpressionForEvaluation() );
+
+ // clear old result, and copy new
+ maNodes.clear();
+ if( mxResult.is() )
+ {
+ // copy node list
+ Reference<XNodeList> xNodeList = mxResult->getNodeList();
+ OSL_ENSURE( xNodeList.is(), "empty object (instead of empty list)" );
+ sal_Int32 nLength = xNodeList.is() ? xNodeList->getLength() : 0;
+ for( sal_Int32 n = 0; n < nLength; n++ )
+ maNodes.push_back( xNodeList->item( n ) );
+ }
+}
+
+
+Reference<XNode> PathExpression::getNode() const
+{
+ Reference<XNode> xResult;
+ if( ! maNodes.empty() )
+ xResult = *maNodes.begin();
+ return xResult;
+}
+
+
+Reference<XNodeList> PathExpression::getXNodeList() const
+{
+ return mxResult.is() ? mxResult->getNodeList() : Reference<XNodeList>();
+}
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/pathexpression.hxx b/forms/source/xforms/pathexpression.hxx
new file mode 100644
index 000000000..319230160
--- /dev/null
+++ b/forms/source/xforms/pathexpression.hxx
@@ -0,0 +1,71 @@
+/* -*- 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 "computedexpression.hxx"
+
+#include <vector>
+
+// forward declaractions
+namespace com::sun::star::xml::dom
+{
+class XNodeList;
+namespace events
+{
+class XEventListener;
+}
+}
+
+namespace xforms
+{
+/** PathExpression represents an XPath Expression and caches results */
+class PathExpression final : public ComputedExpression
+{
+public:
+ typedef std::vector<css::uno::Reference<css::xml::dom::XNode>> NodeVector_t;
+
+private:
+ /// the node-list result from the last bind (cached from mxResult)
+ NodeVector_t maNodes;
+
+ /// get expression for evaluation
+ OUString _getExpressionForEvaluation() const;
+
+public:
+ PathExpression();
+ ~PathExpression();
+
+ /// set the expression string
+ /// (overridden to do remove old listeners)
+ /// (also defines simple expressions)
+ void setExpression(const OUString& rExpression);
+
+ /// evaluate the expression relative to the content node.
+ void evaluate(const xforms::EvaluationContext& rContext);
+
+ // get the result of this expression as node/node list/...
+ css::uno::Reference<css::xml::dom::XNode> getNode() const;
+ const NodeVector_t& getNodeList() const { return maNodes; }
+ css::uno::Reference<css::xml::dom::XNodeList> getXNodeList() const;
+};
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/propertysetbase.cxx b/forms/source/xforms/propertysetbase.cxx
new file mode 100644
index 000000000..0b0787c49
--- /dev/null
+++ b/forms/source/xforms/propertysetbase.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "propertysetbase.hxx"
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Exception;
+using com::sun::star::lang::IllegalArgumentException;
+using com::sun::star::beans::Property;
+using com::sun::star::beans::XPropertySetInfo;
+
+PropertyAccessorBase::~PropertyAccessorBase()
+{
+}
+
+PropertySetBase::PropertySetBase( )
+{
+}
+
+PropertySetBase::~PropertySetBase( )
+{
+}
+
+cppu::IPropertyArrayHelper& SAL_CALL PropertySetBase::getInfoHelper()
+{
+ if ( !m_pProperties )
+ {
+ OSL_ENSURE( !m_aProperties.empty(), "PropertySetBase::getInfoHelper: no registered properties!" );
+ m_pProperties.reset(new cppu::OPropertyArrayHelper( m_aProperties.data(), m_aProperties.size(), false ));
+ }
+ return *m_pProperties;
+}
+
+Reference< XPropertySetInfo > SAL_CALL PropertySetBase::getPropertySetInfo( )
+{
+ return cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
+}
+
+void PropertySetBase::registerProperty( const Property& rProperty,
+ const ::rtl::Reference< PropertyAccessorBase >& rAccessor )
+{
+ OSL_ENSURE(rAccessor,
+ "PropertySetBase::registerProperty: invalid property accessor, this will crash!");
+ m_aAccessors.emplace( rProperty.Handle, rAccessor );
+
+ OSL_ENSURE( rAccessor->isWriteable()
+ == ( ( rProperty.Attributes & css::beans::PropertyAttribute::READONLY ) == 0 ),
+ "PropertySetBase::registerProperty: inconsistence!" );
+
+ m_aProperties.push_back( rProperty );
+}
+
+void PropertySetBase::notifyAndCachePropertyValue( sal_Int32 nHandle )
+{
+ ::osl::ClearableMutexGuard aGuard( GetMutex() );
+
+ PropertyValueCache::iterator aPos = m_aCache.find( nHandle );
+ if ( aPos == m_aCache.end() )
+ { // method has never before been invoked for this property
+ try
+ {
+ // determine the type of this property
+ ::cppu::IPropertyArrayHelper& rPropertyMetaData = getInfoHelper();
+ OUString sPropName;
+ OSL_VERIFY( rPropertyMetaData.fillPropertyMembersByHandle( &sPropName, nullptr, nHandle ) );
+ Property aProperty = rPropertyMetaData.getPropertyByName( sPropName );
+ // default construct a value of this type
+ Any aEmptyValue( nullptr, aProperty.Type );
+ // insert into the cache
+ aPos = m_aCache.emplace( nHandle, aEmptyValue ).first;
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "PropertySetBase::notifyAndCachePropertyValue: this is not expected to fail!" );
+ }
+ }
+ Any aOldValue = aPos->second;
+ // determine the current value
+ Any aNewValue;
+ getFastPropertyValue( aNewValue, nHandle );
+ // remember the old value
+ aPos->second = aNewValue;
+
+ aGuard.clear();
+ if ( aNewValue != aOldValue )
+ firePropertyChange( nHandle, aNewValue, aOldValue );
+}
+
+void PropertySetBase::initializePropertyValueCache( sal_Int32 nHandle )
+{
+ Any aCurrentValue;
+ getFastPropertyValue( aCurrentValue, nHandle );
+
+ ::std::pair< PropertyValueCache::iterator, bool > aInsertResult =
+ m_aCache.emplace( nHandle, aCurrentValue );
+ OSL_ENSURE( aInsertResult.second, "PropertySetBase::initializePropertyValueCache: already cached a value for this property!" );
+}
+
+PropertyAccessorBase& PropertySetBase::locatePropertyHandler( sal_Int32 nHandle ) const
+{
+ PropertyAccessors::const_iterator aPropertyPos = m_aAccessors.find( nHandle );
+ OSL_ENSURE( aPropertyPos != m_aAccessors.end() && aPropertyPos->second,
+ "PropertySetBase::locatePropertyHandler: accessor map is corrupted!" );
+ // neither should this be called for handles where there is no accessor, nor should a
+ // NULL accessor be in the map
+ return *aPropertyPos->second;
+}
+
+sal_Bool SAL_CALL PropertySetBase::convertFastPropertyValue( Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle,
+ const Any& rValue )
+{
+ PropertyAccessorBase& rAccessor = locatePropertyHandler( nHandle );
+ if ( !rAccessor.approveValue( rValue ) )
+ throw IllegalArgumentException( OUString(), *this, 0 );
+
+ rAccessor.getValue( rOldValue );
+ if ( rOldValue != rValue )
+ {
+ rConvertedValue = rValue; // no conversion at all
+ return true;
+ }
+ return false;
+}
+
+void SAL_CALL PropertySetBase::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+{
+ PropertyAccessorBase& rAccessor = locatePropertyHandler( nHandle );
+ rAccessor.setValue( rValue );
+}
+
+void SAL_CALL PropertySetBase::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ PropertyAccessorBase& rAccessor = locatePropertyHandler( nHandle );
+ rAccessor.getValue( rValue );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/propertysetbase.hxx b/forms/source/xforms/propertysetbase.hxx
new file mode 100644
index 000000000..d6ae97405
--- /dev/null
+++ b/forms/source/xforms/propertysetbase.hxx
@@ -0,0 +1,340 @@
+/* -*- 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 <comphelper/propstate.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+// include for inlined helper function below
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <map>
+#include <memory>
+
+namespace com::sun::star::uno {
+ class Any;
+ class RuntimeException;
+ template<class T> class Sequence;
+}
+
+/** base class which encapsulates accessing (reading/writing) concrete property values
+*/
+class PropertyAccessorBase : public salhelper::SimpleReferenceObject
+{
+protected:
+ PropertyAccessorBase() { }
+ virtual ~PropertyAccessorBase() override;
+
+public:
+
+ virtual bool approveValue( const css::uno::Any& rValue ) const = 0;
+ virtual void setValue( const css::uno::Any& rValue ) = 0;
+ virtual void getValue( css::uno::Any& rValue ) const = 0;
+ virtual bool isWriteable() const = 0;
+};
+
+
+/** helper class for implementing property accessors through public member functions
+*/
+template< typename CLASS, typename VALUE, class WRITER, class READER >
+class GenericPropertyAccessor : public PropertyAccessorBase
+{
+public:
+ typedef WRITER Writer;
+ typedef READER Reader;
+
+private:
+ CLASS* m_pInstance;
+ Writer m_pWriter;
+ Reader m_pReader;
+
+public:
+ GenericPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :m_pInstance( pInstance )
+ ,m_pWriter( pWriter )
+ ,m_pReader( pReader )
+ {
+ }
+
+ virtual bool approveValue( const css::uno::Any& rValue ) const override
+ {
+ VALUE aVal;
+ return ( rValue >>= aVal );
+ }
+
+ virtual void setValue( const css::uno::Any& rValue ) override
+ {
+ VALUE aTypedVal = VALUE();
+ OSL_VERIFY( rValue >>= aTypedVal );
+ (m_pInstance->*m_pWriter)( aTypedVal );
+ }
+
+ virtual void getValue( css::uno::Any& rValue ) const override
+ {
+ rValue = css::uno::Any( (m_pInstance->*m_pReader)() );
+ }
+
+ virtual bool isWriteable() const override
+ {
+ return m_pWriter != nullptr;
+ }
+};
+
+/** helper class for implementing property accessors via non-UNO methods
+*/
+template< typename CLASS, typename VALUE >
+class DirectPropertyAccessor
+ :public GenericPropertyAccessor < CLASS
+ , VALUE
+ , void (CLASS::*)( const VALUE& )
+ , VALUE (CLASS::*)() const
+ >
+{
+protected:
+ typedef void (CLASS::*Writer)( const VALUE& );
+ typedef VALUE (CLASS::*Reader)() const;
+public:
+ DirectPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :GenericPropertyAccessor< CLASS, VALUE, Writer, Reader >( pInstance, pWriter, pReader )
+ {
+ }
+};
+
+/** helper class for implementing non-UNO accessors to a boolean property
+*/
+template< typename CLASS >
+class BooleanPropertyAccessor
+ :public GenericPropertyAccessor < CLASS
+ , bool
+ , void (CLASS::*)( bool )
+ , bool (CLASS::*)() const
+ >
+{
+protected:
+ typedef void (CLASS::*Writer)( bool );
+ typedef bool (CLASS::*Reader)() const;
+public:
+ BooleanPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :GenericPropertyAccessor< CLASS, bool, Writer, Reader >( pInstance, pWriter, pReader )
+ {
+ }
+};
+
+/** helper class for implementing property accessors via UNO methods
+*/
+template< typename CLASS, typename VALUE >
+class APIPropertyAccessor
+ :public GenericPropertyAccessor < CLASS
+ , VALUE
+ , void (SAL_CALL CLASS::*)( const VALUE& )
+ , VALUE (SAL_CALL CLASS::*)()
+ >
+{
+protected:
+ typedef void (SAL_CALL CLASS::*Writer)( const VALUE& );
+ typedef VALUE (SAL_CALL CLASS::*Reader)();
+public:
+ APIPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader )
+ :GenericPropertyAccessor< CLASS, VALUE, Writer, Reader >( pInstance, pWriter, pReader )
+ {
+ }
+};
+
+/** bridges two XPropertySet helper implementations
+
+ The <type scope="comphelper">OStatefulPropertySet</type> (basically, the
+ <type scope="cppu">OPropertySetHelper</type>) implements a comprehensive framework
+ for property sets, including property change notifications.
+ However, it lacks some easy possibilities to declare the supported properties.
+ Other helper structs and classes allow for this, but are lacking needed features
+ such as property change notifications.
+
+ The <type>PropertySetBase</type> bridges various implementations,
+ so you have the best of both worlds.
+ */
+class PropertySetBase : public ::comphelper::OStatefulPropertySet
+{
+private:
+ typedef ::std::map< const sal_Int32, ::rtl::Reference< PropertyAccessorBase > > PropertyAccessors;
+ typedef ::std::vector< css::beans::Property > PropertyArray;
+ typedef ::std::map< const sal_Int32, css::uno::Any > PropertyValueCache;
+
+ PropertyArray m_aProperties;
+ std::unique_ptr<cppu::IPropertyArrayHelper> m_pProperties;
+ PropertyAccessors m_aAccessors;
+ PropertyValueCache m_aCache;
+
+protected:
+ PropertySetBase();
+ virtual ~PropertySetBase() override;
+
+ /** registers a new property to be supported by this instance
+ @param rProperty
+ the descriptor for the to-be-supported property
+ @param rAccessor
+ an instance which is able to provide read and possibly write access to
+ the property.
+ @precond
+ Must not be called after any of the property set related UNO interfaces
+ has been used. Usually, you will do a number of <member>registerProperty</member>
+ calls in the constructor of your class.
+ */
+ void registerProperty(
+ const css::beans::Property& rProperty,
+ const ::rtl::Reference< PropertyAccessorBase >& rAccessor
+ );
+
+ /** notifies a change in a given property value, if necessary
+
+ The necessity of the notification is determined by a cached value for the given
+ property. Caching happens after notification.
+
+ That is, when you call <member>notifyAndCachePropertyValue</member> for the first time,
+ a value for the given property is default constructed, and considered to be the "old value".
+ If this value differs from the current value, then this change is notified to all interested
+ listeners. Finally, the current value is remembered.
+
+ Subsequent calls to <member>notifyAndCachePropertyValue</member> use the remembered value as
+ "old value", and from then on behave as the first call.
+
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+
+ @precond
+ our ref count must not be 0. The reason is that during this method's execution,
+ the instance might be acquired and released, which would immediately destroy
+ the instance if it has a ref count of 0.
+
+ @seealso initializePropertyValueCache
+ */
+ void notifyAndCachePropertyValue( sal_Int32 nHandle );
+
+ /** initializes the property value cache for the given property, with its current value
+
+ Usually used to initialize the cache with values which are different from default
+ constructed values. Say you have a boolean property whose initial state
+ is <TRUE/>. Say you call <member>notifyAndCachePropertyValue</member> the first time: it will
+ default construct the "old value" for this property as <FALSE/>, and thus <b>not</b> do
+ any notifications if the "current value" is also <FALSE/> - which might be wrong, since
+ the guessing of the "old value" differed from the real initial value which was <TRUE/>.
+
+ Too confusing? Okay, then just call this method for every property you have.
+
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+ @param rValue
+ the value to cache
+ @seealso notifyAndCachePropertyValue
+ */
+ void initializePropertyValueCache( sal_Int32 nHandle );
+
+ /// OPropertysetHelper methods
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& rConvertedValue, css::uno::Any& rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+
+ virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+public:
+ /// helper struct for granting selective access to some notification-related methods
+ struct NotifierAccess { friend struct PropertyChangeNotifier; private: NotifierAccess() { } };
+ /** retrieves the current property value for the given handle
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+ @see registerProperty
+ */
+ void getCurrentPropertyValueByHandle( sal_Int32 nHandle, css::uno::Any& /* [out] */ rValue, const NotifierAccess& ) const
+ {
+ getFastPropertyValue( rValue, nHandle );
+ }
+
+ /** notifies a change in a given property to all interested listeners
+ */
+ void notifyPropertyChange( sal_Int32 nHandle, const css::uno::Any& rOldValue, const css::uno::Any& rNewValue, const NotifierAccess& ) const
+ {
+ const_cast< PropertySetBase* >( this )->firePropertyChange( nHandle, rNewValue, rOldValue );
+ }
+
+ using ::comphelper::OStatefulPropertySet::getFastPropertyValue;
+
+private:
+ /** locates a property given by handle
+ @param nHandle
+ the handle of the property. Must denote a property supported by this instance, i.e.
+ one previously registered via <member>registerProperty</member>.
+ @see registerProperty
+ */
+ PropertyAccessorBase& locatePropertyHandler( sal_Int32 nHandle ) const;
+};
+
+/** a helper class for notifying property changes in a <type>PropertySetBase</type> instance.
+
+ You can create an instance of this class on the stack of a method which is to programmatically
+ change the value of a property. In its constructor, the instance will acquire the current property
+ value, and in its destructor, it will notify the change of this property's value (if necessary).
+
+ You do not need this class if you are modifying property values by using the X(Fast|Multi)PropertSet
+ methods, since those already care for property notifications. You only need it if you're changing
+ the internal representation of your property directly.
+
+ Also note that usually, notifications in the UNO world should be done without a locked mutex. So
+ if you use this class in conjunction with a <type>MutexGuard</type>, ensure that you <b>first</b>
+ instantiate the <type>PropertyChangeNotifier</type>, and <b>then</b> the <type>MutexGuard</type>,
+ so your mutex is released before the notification happens.
+*/
+struct PropertyChangeNotifier
+{
+private:
+ const PropertySetBase& m_rPropertySet;
+ sal_Int32 m_nHandle;
+ css::uno::Any m_aOldValue;
+
+public:
+ /** constructs a PropertyChangeNotifier
+ @param rPropertySet
+ the property set implementation whose property is going to be changed. Note
+ that this property set implementation must live at least as long as the
+ PropertyChangeNotifier instance does.
+ @param nHandle
+ the handle of the property which is going to be changed. Must be a valid property
+ handle for the given <arg>rPropertySet</arg>
+ */
+ PropertyChangeNotifier( const PropertySetBase& rPropertySet, sal_Int32 nHandle )
+ :m_rPropertySet( rPropertySet )
+ ,m_nHandle( nHandle )
+ {
+ m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, m_aOldValue, PropertySetBase::NotifierAccess() );
+ }
+ ~PropertyChangeNotifier()
+ {
+ css::uno::Any aNewValue;
+ m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, aNewValue, PropertySetBase::NotifierAccess() );
+ if ( aNewValue != m_aOldValue )
+ {
+ m_rPropertySet.notifyPropertyChange( m_nHandle, m_aOldValue, aNewValue, PropertySetBase::NotifierAccess() );
+ }
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/resourcehelper.cxx b/forms/source/xforms/resourcehelper.cxx
new file mode 100644
index 000000000..011d806f5
--- /dev/null
+++ b/forms/source/xforms/resourcehelper.cxx
@@ -0,0 +1,65 @@
+/* -*- 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 "resourcehelper.hxx"
+#include <frm_resource.hxx>
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+
+namespace xforms
+{
+
+OUString getResource(TranslateId pResourceId)
+{
+ return getResource(pResourceId, {}, {}, {});
+}
+
+OUString getResource(TranslateId pResourceId,
+ std::u16string_view rInfo1)
+{
+ return getResource(pResourceId, rInfo1, {}, {});
+}
+
+OUString getResource(TranslateId pResourceId,
+ std::u16string_view rInfo1,
+ std::u16string_view rInfo2)
+{
+ return getResource(pResourceId, rInfo1, rInfo2, {});
+}
+
+OUString getResource(TranslateId pResourceId,
+ std::u16string_view rInfo1,
+ std::u16string_view rInfo2,
+ std::u16string_view rInfo3)
+{
+ OUString sResource = frm::ResourceManager::loadString(pResourceId);
+ OSL_ENSURE( !sResource.isEmpty(), "resource not found?" );
+
+ OUString sString = sResource.replaceAll( "$1", rInfo1 );
+ sString = sString.replaceAll( "$2", rInfo2 );
+ sString = sString.replaceAll( "$3", rInfo3 );
+ return sString;
+}
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/resourcehelper.hxx b/forms/source/xforms/resourcehelper.hxx
new file mode 100644
index 000000000..1453a7e4c
--- /dev/null
+++ b/forms/source/xforms/resourcehelper.hxx
@@ -0,0 +1,41 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <unotools/resmgr.hxx>
+
+namespace xforms
+{
+ /// get a resource string for the current language
+ OUString getResource(TranslateId);
+
+ // overloaded: get a resource string, and substitute parameters
+ OUString getResource(TranslateId, std::u16string_view);
+ OUString getResource(TranslateId, std::u16string_view,
+ std::u16string_view);
+ OUString getResource(TranslateId, std::u16string_view,
+ std::u16string_view,
+ std::u16string_view);
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission.cxx b/forms/source/xforms/submission.cxx
new file mode 100644
index 000000000..d8d81abd5
--- /dev/null
+++ b/forms/source/xforms/submission.cxx
@@ -0,0 +1,609 @@
+/* -*- 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 "submission.hxx"
+
+#include "model.hxx"
+#include "binding.hxx"
+#include "mip.hxx"
+#include "evaluationcontext.hxx"
+#include "submission/submission_put.hxx"
+#include "submission/submission_post.hxx"
+#include "submission/submission_get.hxx"
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/task/XInteractionContinuation.hpp>
+#include <com/sun/star/xforms/InvalidDataOnSubmitException.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <memory>
+#include <string_view>
+
+using com::sun::star::util::VetoException;
+using com::sun::star::form::submission::XSubmissionVetoListener;
+using com::sun::star::lang::WrappedTargetException;
+using com::sun::star::lang::NoSupportException;
+using com::sun::star::task::XInteractionHandler;
+using com::sun::star::task::XInteractionRequest;
+using com::sun::star::task::XInteractionContinuation;
+using com::sun::star::xforms::XModel;
+using com::sun::star::xforms::InvalidDataOnSubmitException;
+using com::sun::star::xml::xpath::XXPathObject;
+using com::sun::star::frame::XFrame;
+using xforms::Submission;
+using xforms::Model;
+using xforms::MIP;
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::dom;
+
+Submission::Submission() :
+ mbIndent(),
+ mbOmitXmlDeclaration(),
+ mbStandalone(),
+ msReplace( "none" )
+{
+ initializePropertySet();
+}
+
+Submission::~Submission() noexcept
+{
+}
+
+
+void Submission::setModel( const Reference<XModel>& xModel )
+{
+ mxModel = xModel;
+}
+
+
+void Submission::setID( const OUString& sID )
+{
+ msID = sID;
+}
+
+
+void Submission::setBind( const OUString& sBind )
+{
+ msBind = sBind;
+}
+
+OUString Submission::getRef() const
+{
+ return maRef.getExpression();
+}
+
+void Submission::setRef( const OUString& sRef )
+{
+ maRef.setExpression( sRef );
+}
+
+
+void Submission::setAction( const OUString& sAction )
+{
+ msAction = sAction;
+}
+
+
+void Submission::setMethod( const OUString& sMethod )
+{
+ msMethod = sMethod;
+}
+
+
+void Submission::setVersion( const OUString& sVersion )
+{
+ msVersion = sVersion;
+}
+
+
+void Submission::setIndent( bool bIndent )
+{
+ mbIndent = bIndent;
+}
+
+
+void Submission::setMediaType( const OUString& sMediaType )
+{
+ msMediaType = sMediaType;
+}
+
+
+void Submission::setEncoding( const OUString& sEncoding )
+{
+ msEncoding = sEncoding;
+}
+
+
+void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration )
+{
+ mbOmitXmlDeclaration = bOmitXmlDeclaration;
+}
+
+
+void Submission::setStandalone( bool bStandalone )
+{
+ mbStandalone = bStandalone;
+}
+
+
+void Submission::setCDataSectionElement( const OUString& sCDataSectionElement )
+{
+ msCDataSectionElement = sCDataSectionElement;
+}
+
+
+void Submission::setReplace( const OUString& sReplace )
+{
+ msReplace = sReplace;
+}
+
+
+void Submission::setSeparator( const OUString& sSeparator )
+{
+ msSeparator = sSeparator;
+}
+
+
+void Submission::setIncludeNamespacePrefixes( const Sequence< OUString >& rIncludeNamespacePrefixes )
+{
+ msIncludeNamespacePrefixes = rIncludeNamespacePrefixes;
+}
+
+bool Submission::doSubmit( const Reference< XInteractionHandler >& xHandler )
+{
+ liveCheck();
+
+ // construct XXPathObject for submission doc; use bind in preference of ref
+ EvaluationContext aEvalContext;
+ ComputedExpression aExpression;
+ if( !msBind.isEmpty() )
+ {
+ Binding* pBinding = comphelper::getFromUnoTunnel<Binding>( mxModel->getBinding(msBind) );
+ if( pBinding != nullptr )
+ {
+ aExpression.setExpression( pBinding->getBindingExpression() );
+ aEvalContext = pBinding->getEvaluationContext();
+ }
+ // TODO: else: illegal binding name -> raise error
+ }
+ else if( !maRef.getExpression().isEmpty() )
+ {
+ aExpression.setExpression( maRef.getExpression() );
+ aEvalContext = comphelper::getFromUnoTunnel<Model>( mxModel )->getEvaluationContext();
+ }
+ else
+ {
+ aExpression.setExpression( "/" );
+ aEvalContext = comphelper::getFromUnoTunnel<Model>( mxModel )->getEvaluationContext();
+ }
+ aExpression.evaluate( aEvalContext );
+ Reference<XXPathObject> xResult = aExpression.getXPath();
+ OSL_ENSURE( xResult.is(), "no result?" );
+
+ // early out if we have not obtained any result
+ if( ! xResult.is() )
+ return false;
+
+
+ // Reference< XNodeList > aList = xResult->getNodeList();
+ OUString aMethod = getMethod();
+
+ // strip whitespace-only text node for get submission
+ Reference< XDocumentFragment > aFragment = createSubmissionDocument(
+ xResult, aMethod.equalsIgnoreAsciiCase("get"));
+
+ // submit result; set encoding, etc.
+ std::unique_ptr<CSubmission> xSubmission;
+ if (aMethod.equalsIgnoreAsciiCase("PUT"))
+ xSubmission.reset(new CSubmissionPut( getAction(), aFragment));
+ else if (aMethod.equalsIgnoreAsciiCase("post"))
+ xSubmission.reset(new CSubmissionPost( getAction(), aFragment));
+ else if (aMethod.equalsIgnoreAsciiCase("get"))
+ xSubmission.reset(new CSubmissionGet( getAction(), aFragment));
+ else
+ {
+ OSL_FAIL("Unsupported xforms submission method");
+ return false;
+ }
+
+ if (!xSubmission->IsWebProtocol())
+ return false;
+
+ CSubmission::SubmissionResult aResult = xSubmission->submit( xHandler );
+
+ if (aResult == CSubmission::SUCCESS)
+ {
+ Reference< XDocument > aInstanceDoc = getInstanceDocument(xResult);
+ aResult = xSubmission->replace(getReplace(), aInstanceDoc, Reference< XFrame >());
+ }
+
+ return ( aResult == CSubmission::SUCCESS );
+}
+
+Sequence<sal_Int8> Submission::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit aImplementationId;
+ return aImplementationId.getSeq();
+}
+
+
+void Submission::liveCheck()
+{
+ bool bValid = mxModel.is();
+
+ if( ! bValid )
+ throw RuntimeException();
+}
+
+Model* Submission::getModelImpl() const
+{
+ Model* pModel = nullptr;
+ if( mxModel.is() )
+ pModel = comphelper::getFromUnoTunnel<Model>( mxModel );
+ return pModel;
+}
+
+
+// Property-Set implementation
+
+
+#define HANDLE_ID 0
+#define HANDLE_Bind 1
+#define HANDLE_Ref 2
+#define HANDLE_Action 3
+#define HANDLE_Method 4
+#define HANDLE_Version 5
+#define HANDLE_Indent 6
+#define HANDLE_MediaType 7
+#define HANDLE_Encoding 8
+#define HANDLE_OmitXmlDeclaration 9
+#define HANDLE_Standalone 10
+#define HANDLE_CDataSectionElement 11
+#define HANDLE_Replace 12
+#define HANDLE_Separator 13
+#define HANDLE_IncludeNamespacePrefixes 14
+#define HANDLE_Model 15
+
+void Submission::initializePropertySet()
+{
+ registerProperty( css::beans::Property("ID", HANDLE_ID, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setID, &Submission::getID) );
+
+ registerProperty( css::beans::Property("Bind", HANDLE_Bind, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setBind, &Submission::getBind) );
+
+ registerProperty( css::beans::Property("Ref", HANDLE_Ref, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setRef, &Submission::getRef) );
+
+ registerProperty( css::beans::Property("Action", HANDLE_Action, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setAction, &Submission::getAction) );
+
+ registerProperty( css::beans::Property("Method", HANDLE_Method, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setMethod, &Submission::getMethod) );
+
+ registerProperty( css::beans::Property("Version", HANDLE_Version, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setVersion, &Submission::getVersion) );
+
+ registerProperty( css::beans::Property("Indent", HANDLE_Indent, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Submission >(this, &Submission::setIndent, &Submission::getIndent));
+
+ registerProperty( css::beans::Property("MediaType", HANDLE_MediaType, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setMediaType, &Submission::getMediaType) );
+
+ registerProperty( css::beans::Property("Encoding", HANDLE_Encoding, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setEncoding, &Submission::getEncoding) );
+
+ registerProperty( css::beans::Property("OmitXmlDeclaration", HANDLE_OmitXmlDeclaration, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Submission >(this, &Submission::setOmitXmlDeclaration, &Submission::getOmitXmlDeclaration));
+
+ registerProperty( css::beans::Property("Standalone", HANDLE_Standalone, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::BOUND ),
+ new BooleanPropertyAccessor< Submission >(this, &Submission::setStandalone, &Submission::getStandalone));
+
+ registerProperty( css::beans::Property("CDataSectionElement", HANDLE_CDataSectionElement, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setCDataSectionElement, &Submission::getCDataSectionElement) );
+
+ registerProperty( css::beans::Property("Replace", HANDLE_Replace, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setReplace, &Submission::getReplace) );
+
+ registerProperty( css::beans::Property("Separator", HANDLE_Separator, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, OUString >(this, &Submission::setSeparator, &Submission::getSeparator) );
+
+ registerProperty( css::beans::Property("IncludeNamespacePrefixes", HANDLE_IncludeNamespacePrefixes, cppu::UnoType<Sequence<OUString>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, Sequence<OUString> >(this, &Submission::setIncludeNamespacePrefixes, &Submission::getIncludeNamespacePrefixes) );
+
+ registerProperty( css::beans::Property("Model", HANDLE_Model, cppu::UnoType<Reference<XModel>>::get(), css::beans::PropertyAttribute::BOUND ),
+ new DirectPropertyAccessor< Submission, Reference<XModel> >(this, &Submission::setModel, &Submission::getModel) );
+
+ initializePropertyValueCache( HANDLE_Indent );
+ initializePropertyValueCache( HANDLE_OmitXmlDeclaration );
+ initializePropertyValueCache( HANDLE_Standalone );
+}
+
+sal_Bool SAL_CALL Submission::convertFastPropertyValue(
+ Any& rConvertedValue, Any& rOldValue, sal_Int32 nHandle, const Any& rValue )
+{
+ if ( nHandle == HANDLE_IncludeNamespacePrefixes )
+ {
+ // for convenience reasons (????), we accept a string which contains
+ // a comma-separated list of namespace prefixes
+ OUString sTokenList;
+ if ( rValue >>= sTokenList )
+ {
+ std::vector< OUString > aPrefixes;
+ sal_Int32 p = 0;
+ while ( p >= 0 )
+ aPrefixes.push_back( sTokenList.getToken( 0, ',', p ) );
+
+ Sequence< OUString > aConvertedPrefixes( aPrefixes.data(), aPrefixes.size() );
+ return PropertySetBase::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, Any( aConvertedPrefixes ) );
+ }
+ }
+
+ return PropertySetBase::convertFastPropertyValue( rConvertedValue, rOldValue, nHandle, rValue );
+}
+
+OUString SAL_CALL Submission::getName()
+{
+ return getID();
+}
+
+void SAL_CALL Submission::setName( const OUString& sID )
+{
+ setID( sID );
+}
+
+
+sal_Int64 SAL_CALL Submission::getSomething(
+ const Sequence<sal_Int8>& aId )
+{
+ return comphelper::getSomethingImpl(aId, this);
+}
+
+
+static OUString lcl_message( std::u16string_view rID, std::u16string_view rText )
+{
+ OUString aMessage = OUString::Concat("XForms submission '") + rID + "' failed" + rText + ".";
+ return aMessage;
+}
+
+void SAL_CALL Submission::submitWithInteraction(
+ const Reference<XInteractionHandler>& _rxHandler )
+{
+ // as long as this class is not really threadsafe, we need to copy
+ // the members we're interested in
+ Reference< XModel > xModel( mxModel );
+ OUString sID( msID );
+
+ if ( !xModel.is() || msID.isEmpty() )
+ throw RuntimeException(
+ "This is not a valid submission object.",
+ *this
+ );
+
+ Model* pModel = comphelper::getFromUnoTunnel<Model>( xModel );
+ OSL_ENSURE( pModel != nullptr, "illegal model?" );
+
+ // #i36765# #i47248# warning on submission of illegal data
+ // check for validity (and query user if invalid)
+ bool bValid = pModel->isValid();
+ if( ! bValid )
+ {
+ InvalidDataOnSubmitException aInvalidDataException(
+ lcl_message(sID, u" due to invalid data" ), *this );
+
+ if( _rxHandler.is() )
+ {
+ // laboriously create interaction request
+ rtl::Reference<comphelper::OInteractionRequest> pRequest
+ = new comphelper::OInteractionRequest(
+ Any( aInvalidDataException ) );
+
+ rtl::Reference<comphelper::OInteractionApprove> pContinue
+ = new comphelper::OInteractionApprove();
+ pRequest->addContinuation( pContinue );
+
+ rtl::Reference<comphelper::OInteractionDisapprove> pCancel
+ = new comphelper::OInteractionDisapprove();
+ pRequest->addContinuation( pCancel );
+
+ // ask the handler...
+ _rxHandler->handle( pRequest );
+ OSL_ENSURE( pContinue->wasSelected() || pCancel->wasSelected(),
+ "handler didn't select" );
+
+ // and continue, if user chose 'continue'
+ if( pContinue->wasSelected() )
+ bValid = true;
+ }
+
+ // abort if invalid (and user didn't tell us to continue)
+ if( ! bValid )
+ throw aInvalidDataException;
+ }
+
+ // attempt submission
+ bool bResult = false;
+ try
+ {
+ bResult = doSubmit( _rxHandler );
+ }
+ catch( const VetoException& )
+ {
+ OSL_FAIL( "Model::submit: Hmm. How can a single submission have a veto right?" );
+ // allowed to leave
+ throw;
+ }
+ catch( const Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ // exception caught: re-throw as wrapped target exception
+ throw WrappedTargetException(
+ lcl_message( sID, u" due to exception being thrown" ),
+ *this, anyEx );
+ }
+
+ if( !bResult )
+ {
+ // other failure: throw wrapped target exception, too.
+ throw WrappedTargetException(
+ lcl_message( sID, std::u16string_view() ), *this, Any() );
+ }
+ mxModel->rebuild();
+}
+
+void SAL_CALL Submission::submit( )
+{
+ submitWithInteraction( nullptr );
+}
+
+void SAL_CALL Submission::addSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
+{
+ // TODO
+ throw NoSupportException();
+}
+
+void SAL_CALL Submission::removeSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
+{
+ // TODO
+ throw NoSupportException();
+}
+
+static bool isIgnorable(const Reference< XNode >& aNode)
+{
+ // ignore whitespace-only textnodes
+ if (aNode->getNodeType() == NodeType_TEXT_NODE)
+ {
+ OUString aTrimmedValue = aNode->getNodeValue().trim();
+ if (aTrimmedValue.isEmpty()) return true;
+ }
+
+ return false;
+}
+
+// recursively copy relevant nodes from A to B
+static void cloneNodes(Model& aModel, const Reference< XNode >& dstParent, const Reference< XNode >& source, bool bRemoveWSNodes)
+{
+ if (!source.is()) return;
+
+ Reference< XNode > cur = source;
+ Reference< XDocument > dstDoc = dstParent->getOwnerDocument();
+ Reference< XNode > imported;
+
+ if (!cur.is())
+ return;
+
+ // is this node relevant?
+ MIP mip = aModel.queryMIP(cur);
+ if(mip.isRelevant() && !(bRemoveWSNodes && isIgnorable(cur)))
+ {
+ imported = dstDoc->importNode(cur, false);
+ imported = dstParent->appendChild(imported);
+ // append source children to new imported parent
+ for( cur = cur->getFirstChild(); cur.is(); cur = cur->getNextSibling() )
+ cloneNodes(aModel, imported, cur, bRemoveWSNodes);
+ }
+}
+Reference< XDocument > Submission::getInstanceDocument(const Reference< XXPathObject >& aObj)
+{
+ using namespace css::xml::xpath;
+ // result
+ Reference< XDocument > aDocument;
+
+ if (aObj->getObjectType() == XPathObjectType_XPATH_NODESET)
+ {
+ Reference< XNodeList > aList = aObj->getNodeList();
+ if (aList->getLength() > 0)
+ aDocument = aList->item(0)->getOwnerDocument();
+ }
+ return aDocument;
+}
+
+Reference< XDocumentFragment > Submission::createSubmissionDocument(const Reference< XXPathObject >& aObj, bool bRemoveWSNodes)
+{
+ using namespace css::xml::xpath;
+ Reference< XDocumentBuilder > aDocBuilder = DocumentBuilder::create(comphelper::getProcessComponentContext());
+ Reference< XDocument > aDocument = aDocBuilder->newDocument();
+ Reference< XDocumentFragment > aFragment = aDocument->createDocumentFragment();
+
+
+ if (aObj->getObjectType() == XPathObjectType_XPATH_NODESET)
+ {
+ Reference< XNodeList > aList = aObj->getNodeList();
+ Reference< XNode > aListItem;
+ for (sal_Int32 i=0; i < aList->getLength(); i++)
+ {
+ aListItem = aList->item(i);
+ if (aListItem->getNodeType()==NodeType_DOCUMENT_NODE)
+ aListItem = (Reference< XDocument >(aListItem, UNO_QUERY))->getDocumentElement();
+ // copy relevant nodes from instance into fragment
+ cloneNodes(*getModelImpl(), aFragment, aListItem, bRemoveWSNodes);
+ }
+ }
+ return aFragment;
+}
+
+// some forwarding: XPropertySet is implemented in our base class,
+// but also available as base of XSubmission
+Reference< css::beans::XPropertySetInfo > SAL_CALL Submission::getPropertySetInfo( )
+{
+ return PropertySetBase::getPropertySetInfo();
+}
+void SAL_CALL Submission::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ PropertySetBase::setPropertyValue( aPropertyName, aValue );
+}
+Any SAL_CALL Submission::getPropertyValue( const OUString& PropertyName )
+{
+ return PropertySetBase::getPropertyValue( PropertyName );
+}
+void SAL_CALL Submission::addPropertyChangeListener( const OUString& aPropertyName, const Reference< css::beans::XPropertyChangeListener >& xListener )
+{
+ PropertySetBase::addPropertyChangeListener( aPropertyName, xListener );
+}
+void SAL_CALL Submission::removePropertyChangeListener( const OUString& aPropertyName, const Reference< css::beans::XPropertyChangeListener >& aListener )
+{
+ PropertySetBase::removePropertyChangeListener( aPropertyName, aListener );
+}
+void SAL_CALL Submission::addVetoableChangeListener( const OUString& PropertyName, const Reference< css::beans::XVetoableChangeListener >& aListener )
+{
+ PropertySetBase::addVetoableChangeListener( PropertyName, aListener );
+}
+void SAL_CALL Submission::removeVetoableChangeListener( const OUString& PropertyName, const Reference< css::beans::XVetoableChangeListener >& aListener )
+{
+ PropertySetBase::removeVetoableChangeListener( PropertyName, aListener );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission.hxx b/forms/source/xforms/submission.hxx
new file mode 100644
index 000000000..1e7f17e4c
--- /dev/null
+++ b/forms/source/xforms/submission.hxx
@@ -0,0 +1,232 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include "propertysetbase.hxx"
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+#include <com/sun/star/xml/xpath/XXPathObject.hpp>
+#include <com/sun/star/xforms/XSubmission.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "computedexpression.hxx"
+
+// forward declaractions
+namespace com::sun::star {
+ namespace xforms { class XModel; }
+ namespace uno { class Any; }
+ namespace beans { class UnknownPropertyException;
+ class PropertyVetoException; }
+ namespace lang { class IllegalArgumentException;
+ class WrappedTargetException; }
+}
+namespace xforms { class Model; }
+
+
+namespace xforms
+{
+
+/** An XForms submission.
+ *
+ * See http://www.w3.org/TR/xforms/ for more information.
+ */
+typedef cppu::ImplInheritanceHelper<
+ PropertySetBase,
+ css::lang::XUnoTunnel,
+ css::xforms::XSubmission
+> Submission_t;
+
+class Submission : public Submission_t
+{
+ // submission properties
+ OUString msID;
+ OUString msBind;
+ ComputedExpression maRef;
+ OUString msAction;
+ OUString msMethod;
+ OUString msVersion;
+ bool mbIndent;
+ OUString msMediaType;
+ OUString msEncoding;
+ bool mbOmitXmlDeclaration;
+ bool mbStandalone;
+ OUString msCDataSectionElement;
+ OUString msReplace;
+ OUString msSeparator;
+ css::uno::Sequence< OUString > msIncludeNamespacePrefixes;
+
+private:
+
+ /// the Model to which this Submission belongs; may be NULL
+ css::uno::Reference<css::xforms::XModel> mxModel;
+
+ // this will extract the document from the model that will be submitted
+ css::uno::Reference< css::xml::dom::XDocumentFragment >
+ createSubmissionDocument(const css::uno::Reference< css::xml::xpath::XXPathObject >& aObject,
+ bool bRemoveWSNodes);
+ static css::uno::Reference< css::xml::dom::XDocument >
+ getInstanceDocument(const css::uno::Reference< css::xml::xpath::XXPathObject >& aObject);
+
+public:
+ Submission();
+ virtual ~Submission() noexcept override;
+
+
+ // property methods: get/set value
+
+
+ /// get XForms model
+ css::uno::Reference<css::xforms::XModel>
+ getModel() const { return mxModel;}
+
+ /// set XForms model
+ void setModel(
+ const css::uno::Reference<css::xforms::XModel>& );
+
+ OUString getID() const { return msID;} /// get ID for this submission
+ void setID( const OUString& ); /// set ID for this submission
+
+ OUString getBind() const { return msBind;}
+ void setBind( const OUString& );
+
+ OUString getRef() const;
+ void setRef( const OUString& );
+
+ OUString getAction() const { return msAction;}
+ void setAction( const OUString& );
+
+ OUString getMethod() const { return msMethod;}
+ void setMethod( const OUString& );
+
+ OUString getVersion() const { return msVersion;}
+ void setVersion( const OUString& );
+
+ bool getIndent() const { return mbIndent;}
+ void setIndent( bool );
+
+ OUString getMediaType() const { return msMediaType;}
+ void setMediaType( const OUString& );
+
+ OUString getEncoding() const { return msEncoding;}
+ void setEncoding( const OUString& );
+
+ bool getOmitXmlDeclaration() const { return mbOmitXmlDeclaration;}
+ void setOmitXmlDeclaration( bool );
+
+ bool getStandalone() const { return mbStandalone;}
+ void setStandalone( bool );
+
+ OUString getCDataSectionElement() const { return msCDataSectionElement;}
+ void setCDataSectionElement( const OUString& );
+
+ OUString getReplace() const { return msReplace;}
+ void setReplace( const OUString& );
+
+ OUString getSeparator() const { return msSeparator;}
+ void setSeparator( const OUString& );
+
+ css::uno::Sequence< OUString > getIncludeNamespacePrefixes() const { return msIncludeNamespacePrefixes;}
+ void setIncludeNamespacePrefixes( const css::uno::Sequence< OUString >& );
+
+ /** perform the submission
+ * @returns if submission was successful */
+ bool doSubmit( const css::uno::Reference< css::task::XInteractionHandler >& aHandler );
+
+ // helpers for UNO tunnel
+ static css::uno::Sequence<sal_Int8> getUnoTunnelId();
+
+private:
+
+ /// check whether object is live, and throw suitable exception if not
+ /// (to be used be API methods before acting on the object)
+ ///
+ /// @throws css::uno::RuntimeException
+ void liveCheck();
+
+ /// get the model implementation
+ xforms::Model* getModelImpl() const;
+
+protected:
+
+
+ // XPropertySet & friends:
+ // implement abstract methods from PropertySetHelper
+
+
+ virtual sal_Bool SAL_CALL convertFastPropertyValue(
+ css::uno::Any& rConvertedValue,
+ css::uno::Any& rOldValue,
+ sal_Int32 nHandle,
+ const css::uno::Any& rValue ) override;
+
+private:
+ void initializePropertySet();
+
+
+public:
+
+
+ // XNamed:
+ // get/set name
+
+
+ virtual OUString SAL_CALL getName() override;
+
+ virtual void SAL_CALL setName( const OUString& ) override;
+
+
+ // XUnoTunnel
+
+
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence<sal_Int8>& ) override;
+
+
+ // XSubmission
+
+
+ virtual void SAL_CALL submit( ) override;
+ virtual void SAL_CALL submitWithInteraction(
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler ) override;
+ virtual void SAL_CALL addSubmissionVetoListener(
+ const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+ virtual void SAL_CALL removeSubmissionVetoListener(
+ const css::uno::Reference< css::form::submission::XSubmissionVetoListener >& listener ) override;
+
+
+ // XPropertySet
+ // (need to disambiguate this)
+
+ 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;
+};
+
+
+} // namespace xforms
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/replace.cxx b/forms/source/xforms/submission/replace.cxx
new file mode 100644
index 000000000..e6154ef61
--- /dev/null
+++ b/forms/source/xforms/submission/replace.cxx
@@ -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 .
+ */
+
+#include <memory>
+#include "submission.hxx"
+#include "serialization_app_xml.hxx"
+
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::ucb;
+using namespace com::sun::star::frame;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::task;
+using namespace com::sun::star::xml::dom;
+
+CSubmission::SubmissionResult CSubmission::replace(std::u16string_view aReplace, const Reference<XDocument>& aDocument, const Reference<XFrame>& aFrame)
+{
+ if (!m_aResultStream.is())
+ return CSubmission::UNKNOWN_ERROR;
+
+ try {
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ if (o3tl::equalsIgnoreAsciiCase(aReplace, u"all")
+ || o3tl::equalsIgnoreAsciiCase(aReplace, u"document")) {
+ Reference< XComponentLoader > xLoader;
+ if (aFrame.is())
+ xLoader.set(aFrame, UNO_QUERY);
+
+ if (!xLoader.is())
+ xLoader.set( Desktop::create(xContext), UNO_QUERY_THROW);
+
+ // open the stream from the result...
+ // build media descriptor
+ Sequence< PropertyValue > descriptor{
+ PropertyValue("InputStream",
+ -1, Any(m_aResultStream), PropertyState_DIRECT_VALUE),
+ PropertyValue("ReadOnly",
+ -1, Any(true), PropertyState_DIRECT_VALUE)
+ };
+
+ OUString aURL = m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ xLoader->loadComponentFromURL(aURL, "_default", FrameSearchFlag::ALL, descriptor);
+
+ return CSubmission::SUCCESS;
+
+ } else if (o3tl::equalsIgnoreAsciiCase(aReplace, u"instance")) {
+ if (aDocument.is()) {
+ // parse the result stream into a new document
+ Reference< XDocumentBuilder > xBuilder(DocumentBuilder::create(xContext));
+ Reference< XDocument > aNewDocument = xBuilder->parse(m_aResultStream);
+
+ if (aNewDocument.is()) {
+ // and replace the content of the current instance
+ Reference< XElement > oldRoot = aDocument->getDocumentElement();
+ Reference< XElement > newRoot = aNewDocument->getDocumentElement();
+
+ Reference< XNode > aImportedNode = aDocument->importNode(newRoot, true);
+ aDocument->replaceChild(aImportedNode, oldRoot);
+ return CSubmission::SUCCESS;
+ } else {
+ return CSubmission::UNKNOWN_ERROR;
+ }
+ } else {
+ // nothing to replace
+ return CSubmission::UNKNOWN_ERROR;
+ }
+ } else if (o3tl::equalsIgnoreAsciiCase(aReplace, u"none")) {
+ // do nothing \o/
+ return CSubmission::SUCCESS;
+ }
+ } catch (const Exception&) {
+ TOOLS_WARN_EXCEPTION( "forms.xforms", "Exception during replace");
+ }
+ return CSubmission::UNKNOWN_ERROR;
+}
+
+::std::unique_ptr< CSerialization > CSubmission::createSerialization(const Reference< XInteractionHandler >& _xHandler,Reference<XCommandEnvironment>& _rOutEnv)
+{
+ // PUT always uses application/xml
+ ::std::unique_ptr< CSerialization > apSerialization(new CSerializationAppXML());
+ apSerialization->setSource(m_aFragment);
+ apSerialization->serialize();
+
+ // create a commandEnvironment and use the default interaction handler
+ rtl::Reference<CCommandEnvironmentHelper> pHelper = new CCommandEnvironmentHelper;
+ if( _xHandler.is() )
+ pHelper->m_aInteractionHandler = _xHandler;
+ else
+ pHelper->m_aInteractionHandler.set(
+ InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW);
+
+ rtl::Reference<CProgressHandlerHelper> pProgressHelper = new CProgressHandlerHelper;
+ pHelper->m_aProgressHandler.set(pProgressHelper);
+
+ // UCB has ownership of environment...
+ _rOutEnv = pHelper;
+ return apSerialization;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization.hxx b/forms/source/xforms/submission/serialization.hxx
new file mode 100644
index 000000000..19f072b0b
--- /dev/null
+++ b/forms/source/xforms/submission/serialization.hxx
@@ -0,0 +1,60 @@
+/* -*- 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/uno/Reference.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+
+/**
+Serialize an XObject
+*/
+
+class CSerialization
+{
+protected:
+ css::uno::Reference<css::xml::dom::XDocumentFragment> m_aFragment;
+
+public:
+ virtual ~CSerialization() {}
+
+ /**
+ sets the XObject that is to serialized
+ */
+ void setSource(const css::uno::Reference<css::xml::dom::XDocumentFragment>& aFragment)
+ {
+ m_aFragment = aFragment;
+ }
+
+ /**
+ start the serialization process
+ */
+ virtual void serialize() = 0;
+
+ /**
+ get the serialized bytes.
+ reads up to buffer->getLength() bytes and returns the number of
+ bytes read.
+ returns -1 on error
+ */
+ virtual css::uno::Reference<css::io::XInputStream> getInputStream() = 0;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_app_xml.cxx b/forms/source/xforms/submission/serialization_app_xml.cxx
new file mode 100644
index 000000000..61729d027
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_app_xml.cxx
@@ -0,0 +1,126 @@
+/* -*- 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 "serialization_app_xml.hxx"
+
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <comphelper/processfactory.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::xml::dom::DocumentBuilder;
+using ::com::sun::star::xml::dom::XNode;
+using ::com::sun::star::xml::dom::XDocument;
+using ::com::sun::star::xml::sax::XSAXSerializable;
+using ::com::sun::star::beans::StringPair;
+using ::com::sun::star::xml::dom::NodeType_DOCUMENT_NODE;
+using ::com::sun::star::xml::dom::NodeType_ELEMENT_NODE;
+using ::com::sun::star::xml::dom::XDocumentBuilder;
+using ::com::sun::star::xml::sax::Writer;
+using ::com::sun::star::xml::sax::XDocumentHandler;
+
+CSerializationAppXML::CSerializationAppXML()
+ : m_xBuffer(css::io::Pipe::create(comphelper::getProcessComponentContext()))
+{
+}
+
+Reference< css::io::XInputStream >
+CSerializationAppXML::getInputStream()
+{
+ // The pipes output is provided through it's
+ // XOutputStream interface aspect
+ return m_xBuffer;
+}
+
+void
+CSerializationAppXML::serialize_node(const Reference< XNode >& rNode)
+{
+ try
+ {
+ Reference< XSAXSerializable > xSerializer( rNode, UNO_QUERY );
+ if ( !xSerializer.is() )
+ {
+ // ensure we have a "real" node
+ Reference< XNode > xNode = rNode;
+ if ( xNode->getNodeType() == NodeType_DOCUMENT_NODE )
+ {
+ Reference< XDocument > const xDoc( xNode, UNO_QUERY_THROW );
+ xNode.set( xDoc->getDocumentElement(), UNO_QUERY_THROW );
+ }
+ ENSURE_OR_RETURN_VOID( xNode->getNodeType() == NodeType_ELEMENT_NODE,
+ "CSerializationAppXML::serialize_node: invalid node type!" );
+
+ // create a new document
+ Reference< XDocumentBuilder > const xDocBuilder = DocumentBuilder::create( comphelper::getProcessComponentContext() );
+ Reference< XDocument > const xDocument( xDocBuilder->newDocument(), UNO_SET_THROW );
+
+ // copy the to-be-serialized node
+ Reference< XNode > const xImportedNode( xDocument->importNode( xNode, true ), UNO_SET_THROW );
+ xDocument->appendChild( xImportedNode );
+
+ // ask the doc for the serializer
+ xSerializer.set( xDocument, UNO_QUERY );
+ }
+
+ ENSURE_OR_RETURN_VOID( xSerializer.is(),
+ "CSerializationAppXML::serialize_node: no serialization access to the node/document!" );
+
+ // create a SAXWriter to take the serialization events, and connect it to our pipe
+ Reference< css::xml::sax::XWriter > const xSaxWriter = Writer::create( comphelper::getProcessComponentContext() );
+ xSaxWriter->setOutputStream( Reference< css::io::XOutputStream >( m_xBuffer, UNO_QUERY_THROW) );
+
+ // do the serialization
+ xSerializer->serialize( Reference< XDocumentHandler >(xSaxWriter, UNO_QUERY_THROW), Sequence< StringPair >() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("forms.xforms");
+ }
+}
+
+void
+CSerializationAppXML::serialize()
+{
+ if (!m_aFragment.is()) return;
+
+ Reference< XNode > cur = m_aFragment->getFirstChild();
+ while (cur.is())
+ {
+ serialize_node(cur);
+ cur = cur->getNextSibling();
+ }
+ m_xBuffer->closeOutput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_app_xml.hxx b/forms/source/xforms/submission/serialization_app_xml.hxx
new file mode 100644
index 000000000..51fb1eb54
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_app_xml.hxx
@@ -0,0 +1,40 @@
+/* -*- 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/io/XPipe.hpp>
+
+#include "serialization.hxx"
+
+class CSerializationAppXML : public CSerialization
+{
+private:
+ css::uno::Reference<css::io::XPipe> m_xBuffer;
+
+ void serialize_node(const css::uno::Reference<css::xml::dom::XNode>& aNode);
+
+public:
+ CSerializationAppXML();
+
+ virtual void serialize() override;
+ virtual css::uno::Reference<css::io::XInputStream> getInputStream() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_urlencoded.cxx b/forms/source/xforms/submission/serialization_urlencoded.cxx
new file mode 100644
index 000000000..22a625ccb
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_urlencoded.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/xml/xpath/XPathObjectType.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XText.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/NodeType.hpp>
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/strbuf.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <stdio.h>
+
+#include "serialization_urlencoded.hxx"
+
+using namespace css::uno;
+using namespace css::io;
+using namespace css::xml::xpath;
+using namespace css::xml::dom;
+
+CSerializationURLEncoded::CSerializationURLEncoded()
+ : m_aPipe(Pipe::create(comphelper::getProcessComponentContext()))
+{
+}
+
+
+/*
+ rfc2396
+ reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ "$" | ","
+ mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+ unreserved = alphanum | mark
+*/
+bool CSerializationURLEncoded::is_unreserved(char c)
+{
+ if (rtl::isAsciiAlphanumeric(static_cast<unsigned char>(c)))
+ return true;
+ switch (c) {
+ case '-':
+ case '_':
+ case '.':
+ case '!':
+ case '~':
+ case '*':
+ case '\'':
+ case '(':
+ case ')':
+ return true;
+ }
+ return false;
+}
+void CSerializationURLEncoded::encode_and_append(
+ std::u16string_view aString, OStringBuffer& aBuffer)
+{
+ OString utf8String = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
+ const sal_uInt8 *pString = reinterpret_cast< const sal_uInt8 * >( utf8String.getStr() );
+ char tmpChar[4];
+
+ while( *pString != 0)
+ {
+ if( *pString < 0x80 )
+ {
+ if ( is_unreserved(*pString) ) {
+ aBuffer.append(char(*pString));
+ } else if (*pString == 0x20) {
+ aBuffer.append('+');
+ } else if (*pString == 0x0d && *(pString+1) == 0x0a) {
+ aBuffer.append("%0D%0A");
+ pString++;
+ } else if (*pString == 0x0a) {
+ aBuffer.append("%0D%0A");
+ } else {
+ snprintf(tmpChar, 4, "%%%X", *pString % 0x100);
+ aBuffer.append(tmpChar);
+ }
+ } else {
+ snprintf(tmpChar, 4, "%%%X", *pString % 0x100);
+ aBuffer.append(tmpChar);
+ while (*pString >= 0x80) {
+ // continuation...
+ pString++;
+ snprintf(tmpChar, 4, "%%%X", *pString % 0x100);
+ aBuffer.append(tmpChar);
+ }
+ }
+ pString++;
+ }
+}
+
+void CSerializationURLEncoded::serialize_node(const Reference< XNode >& aNode)
+{
+ // serialize recursive
+ // every element node E that has a text child T will be serialized in document order
+ // <E1>T1<E2>T2</E2></E1><E3>T3</E3> -> E1=T2&E2=T2&E3=T3 (En := local name)
+
+ // this node
+ Reference< XNodeList > aChildList = aNode->getChildNodes();
+ Reference< XNode > aChild;
+ // is this an element node?
+ if (aNode->getNodeType() == NodeType_ELEMENT_NODE)
+ {
+ OUString aName = aNode->getNodeName();
+ // find any text children
+ OUStringBuffer aValue;
+ Reference< XText > aText;
+ for(sal_Int32 i=0; i < aChildList->getLength(); i++)
+ {
+ aChild = aChildList->item(i);
+ if (aChild->getNodeType() == NodeType_TEXT_NODE)
+ {
+ aText.set(aChild, UNO_QUERY);
+ aValue.append(aText->getData());
+ }
+ }
+
+ // found anything?
+ if (!aValue.isEmpty())
+ {
+ OUString aUnencValue = aValue.makeStringAndClear();
+ OStringBuffer aEncodedBuffer;
+ encode_and_append(aName, aEncodedBuffer);
+ aEncodedBuffer.append("=");
+ encode_and_append(aUnencValue, aEncodedBuffer);
+ aEncodedBuffer.append("&");
+ sal_Int8 const *pData = reinterpret_cast<sal_Int8 const *>(aEncodedBuffer.getStr());
+ Sequence< sal_Int8 > sData(pData, aEncodedBuffer.getLength());
+ m_aPipe->writeBytes(sData);
+ }
+ }
+
+ // element children...
+ for(sal_Int32 i=0; i < aChildList->getLength(); i++)
+ {
+ aChild = aChildList->item(i);
+ // if this is an element node, it might be a candidate for serialization
+ if (aChild.is() && aChild->getNodeType() == NodeType_ELEMENT_NODE)
+ serialize_node(aChild);
+ }
+}
+
+void CSerializationURLEncoded::serialize()
+{
+
+ // output stream to the pipe buffer
+
+ css::uno::Reference< css::xml::dom::XNode > cur = m_aFragment->getFirstChild();
+ while (cur.is())
+ {
+ serialize_node(cur);
+ cur = cur->getNextSibling();
+ }
+ m_aPipe->closeOutput();
+}
+
+Reference< XInputStream > CSerializationURLEncoded::getInputStream()
+{
+ return m_aPipe;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/serialization_urlencoded.hxx b/forms/source/xforms/submission/serialization_urlencoded.hxx
new file mode 100644
index 000000000..2ae91c1f1
--- /dev/null
+++ b/forms/source/xforms/submission/serialization_urlencoded.hxx
@@ -0,0 +1,47 @@
+/* -*- 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 <string_view>
+
+#include <com/sun/star/io/XPipe.hpp>
+
+#include <rtl/strbuf.hxx>
+
+#include "serialization.hxx"
+
+class CSerializationURLEncoded : public CSerialization
+{
+private:
+ css::uno::Reference<css::io::XPipe> m_aPipe;
+
+ static bool is_unreserved(char);
+ static void encode_and_append(std::u16string_view aString, OStringBuffer& aBuffer);
+ void serialize_node(const css::uno::Reference<css::xml::dom::XNode>& aNode);
+
+public:
+ CSerializationURLEncoded();
+ virtual void serialize() override;
+ virtual css::uno::Reference<css::io::XInputStream> getInputStream() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission.hxx b/forms/source/xforms/submission/submission.hxx
new file mode 100644
index 000000000..ac80947aa
--- /dev/null
+++ b/forms/source/xforms/submission/submission.hxx
@@ -0,0 +1,137 @@
+/* -*- 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 <tools/urlobj.hxx>
+#include <osl/conditn.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/xml/dom/XDocumentFragment.hpp>
+
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include "serialization.hxx"
+
+#include <memory>
+
+class CSubmissionPut;
+class CSubmissionPost;
+class CSubmissionGet;
+
+class CCommandEnvironmentHelper final : public cppu::WeakImplHelper< css::ucb::XCommandEnvironment >
+{
+ friend class CSubmissionPut;
+ friend class CSubmissionPost;
+ friend class CSubmissionGet;
+ friend class CSubmission;
+
+ css::uno::Reference< css::task::XInteractionHandler > m_aInteractionHandler;
+ css::uno::Reference< css::ucb::XProgressHandler > m_aProgressHandler;
+
+public:
+ virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override
+ {
+ return m_aInteractionHandler;
+ }
+ virtual css::uno::Reference< css::ucb::XProgressHandler > SAL_CALL getProgressHandler() override
+ {
+ return m_aProgressHandler;
+ }
+};
+
+class CProgressHandlerHelper final : public cppu::WeakImplHelper< css::ucb::XProgressHandler >
+{
+ friend class CSubmissionPut;
+ friend class CSubmissionPost;
+ friend class CSubmissionGet;
+ osl::Condition m_cFinished;
+ osl::Mutex m_mLock;
+ sal_Int32 m_count;
+public:
+ CProgressHandlerHelper()
+ : m_count(0)
+ {}
+ virtual void SAL_CALL push( const css::uno::Any& /*aStatus*/) override
+ {
+ m_mLock.acquire();
+ m_count++;
+ m_mLock.release();
+ }
+ virtual void SAL_CALL update(const css::uno::Any& /*aStatus*/) override
+ {
+ }
+ virtual void SAL_CALL pop() override
+ {
+ m_mLock.acquire();
+ m_count--;
+ if (m_count == 0)
+ m_cFinished.set();
+ m_mLock.release();
+ }
+};
+
+class CSubmission
+{
+
+protected:
+ INetURLObject m_aURLObj;
+ css::uno::Reference< css::xml::dom::XDocumentFragment > m_aFragment;
+ css::uno::Reference< css::io::XInputStream > m_aResultStream;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ ::std::unique_ptr< CSerialization > createSerialization(const css::uno::Reference< css::task::XInteractionHandler >& aHandler
+ ,css::uno::Reference<css::ucb::XCommandEnvironment>& _rOutEnv);
+
+public:
+ enum SubmissionResult {
+ SUCCESS,
+ UNKNOWN_ERROR
+ };
+
+ CSubmission(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : m_aURLObj(aURL)
+ , m_aFragment(aFragment)
+ , m_xContext(::comphelper::getProcessComponentContext())
+ {}
+
+ bool IsWebProtocol() const
+ {
+ INetProtocol eProtocol = m_aURLObj.GetProtocol();
+ return eProtocol == INetProtocol::Http || eProtocol == INetProtocol::Https;
+ }
+
+ virtual ~CSubmission() {}
+
+ virtual SubmissionResult submit(const css::uno::Reference< css::task::XInteractionHandler >& ) = 0;
+
+ SubmissionResult replace(std::u16string_view, const css::uno::Reference< css::xml::dom::XDocument >&, const css::uno::Reference< css::frame::XFrame>&);
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_get.cxx b/forms/source/xforms/submission/submission_get.cxx
new file mode 100644
index 000000000..5a82aeae0
--- /dev/null
+++ b/forms/source/xforms/submission/submission_get.cxx
@@ -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 .
+ */
+
+
+#include "submission_get.hxx"
+#include "serialization_urlencoded.hxx"
+
+#include <rtl/strbuf.hxx>
+#include <osl/diagnose.h>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace css::uno;
+using namespace css::ucb;
+using namespace css::task;
+using namespace css::io;
+using namespace osl;
+using namespace ucbhelper;
+
+
+CSubmissionGet::CSubmissionGet(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : CSubmission(aURL, aFragment)
+{
+}
+
+CSubmission::SubmissionResult CSubmissionGet::submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler)
+{
+ // GET always uses application/x-www-formurlencoded
+ CSerializationURLEncoded aSerialization;
+ aSerialization.setSource(m_aFragment);
+ aSerialization.serialize();
+
+ css::uno::Reference< XInputStream > aInStream = aSerialization.getInputStream();
+
+ // create a commandEnvironment and use the default interaction handler
+ rtl::Reference<CCommandEnvironmentHelper> pHelper = new CCommandEnvironmentHelper;
+ if( aInteractionHandler.is() )
+ pHelper->m_aInteractionHandler = aInteractionHandler;
+ else
+ pHelper->m_aInteractionHandler.set(
+ css::task::InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW);
+ rtl::Reference<CProgressHandlerHelper> pProgressHelper = new CProgressHandlerHelper;
+ pHelper->m_aProgressHandler.set(pProgressHelper);
+
+ // UCB has ownership of environment...
+ css::uno::Reference< XCommandEnvironment > aEnvironment(pHelper);
+
+ // append query string to the URL
+ try {
+ OStringBuffer aUTF8QueryURL(OUStringToOString(m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ RTL_TEXTENCODING_UTF8));
+ OStringBuffer aQueryString;
+
+ const sal_Int32 size = 1024;
+ sal_Int32 n = 0;
+ Sequence< sal_Int8 > aByteBuffer(size);
+ while ((n = aInStream->readSomeBytes(aByteBuffer, size-1)) != 0)
+ aQueryString.append(reinterpret_cast<char const *>(aByteBuffer.getConstArray()), n);
+ if (!aQueryString.isEmpty() && m_aURLObj.GetProtocol() != INetProtocol::File)
+ {
+ aUTF8QueryURL.append('?');
+ aUTF8QueryURL.append(aQueryString);
+ }
+ OUString aQueryURL = OStringToOUString(aUTF8QueryURL.makeStringAndClear(), RTL_TEXTENCODING_UTF8);
+ ucbhelper::Content aContent(aQueryURL, aEnvironment, m_xContext);
+ css::uno::Reference< XOutputStream > aPipe( css::io::Pipe::create(m_xContext), UNO_QUERY_THROW );
+ if (!aContent.openStream(aPipe))
+ return UNKNOWN_ERROR;
+ // get reply
+ try {
+ m_aResultStream = aContent.openStream();
+ } catch (const Exception&) {
+ OSL_FAIL("Cannot open reply stream from content");
+ }
+ } catch (const Exception&)
+ {
+ // XXX
+ TOOLS_WARN_EXCEPTION( "forms.misc", "Exception during UCB operation.");
+ return UNKNOWN_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_get.hxx b/forms/source/xforms/submission/submission_get.hxx
new file mode 100644
index 000000000..086fab25b
--- /dev/null
+++ b/forms/source/xforms/submission/submission_get.hxx
@@ -0,0 +1,33 @@
+/* -*- 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 "submission.hxx"
+
+class CSubmissionGet : public CSubmission
+{
+public:
+ CSubmissionGet(std::u16string_view aURL,
+ const css::uno::Reference<css::xml::dom::XDocumentFragment>& aFragment);
+ virtual SubmissionResult
+ submit(const css::uno::Reference<css::task::XInteractionHandler>& aInteractionHandler) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_post.cxx b/forms/source/xforms/submission/submission_post.cxx
new file mode 100644
index 000000000..89ffd2323
--- /dev/null
+++ b/forms/source/xforms/submission/submission_post.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 "submission_post.hxx"
+
+#include <osl/diagnose.h>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/activedatasink.hxx>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <tools/diagnose_ex.h>
+
+using namespace css::uno;
+using namespace css::ucb;
+using namespace css::task;
+using namespace css::io;
+using namespace osl;
+using namespace ucbhelper;
+
+
+CSubmissionPost::CSubmissionPost(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : CSubmission(aURL, aFragment)
+{
+}
+
+CSubmission::SubmissionResult CSubmissionPost::submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler)
+{
+ // PUT always uses application/xml
+ css::uno::Reference< XCommandEnvironment > aEnvironment;
+ std::unique_ptr< CSerialization > apSerialization(createSerialization(aInteractionHandler,aEnvironment));
+
+ try {
+ ucbhelper::Content aContent(m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aEnvironment, comphelper::getProcessComponentContext());
+
+ // use post command
+ PostCommandArgument2 aPostArgument;
+ aPostArgument.Source = apSerialization->getInputStream();
+ css::uno::Reference< XActiveDataSink > aSink(new ucbhelper::ActiveDataSink);
+ aPostArgument.Sink = aSink;
+ aPostArgument.MediaType = "application/xml";
+ aPostArgument.Referer.clear();
+ Any aCommandArgument;
+ aCommandArgument <<= aPostArgument;
+ aContent.executeCommand( "post", aCommandArgument);
+
+ try {
+ m_aResultStream = aSink->getInputStream();
+ } catch (const Exception&) {
+ OSL_FAIL("Cannot open reply stream from content");
+ }
+ } catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "forms.misc", "Exception during UCB operation.");
+ return UNKNOWN_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_post.hxx b/forms/source/xforms/submission/submission_post.hxx
new file mode 100644
index 000000000..0fb63472b
--- /dev/null
+++ b/forms/source/xforms/submission/submission_post.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "submission.hxx"
+
+class CSubmissionPost : public CSubmission
+{
+public:
+ CSubmissionPost(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment);
+ virtual SubmissionResult submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_put.cxx b/forms/source/xforms/submission/submission_put.cxx
new file mode 100644
index 000000000..678cd7921
--- /dev/null
+++ b/forms/source/xforms/submission/submission_put.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+
+#include "submission_put.hxx"
+
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/content.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace css::uno;
+using namespace css::ucb;
+using namespace css::task;
+using namespace css::io;
+using namespace osl;
+using namespace ucbhelper;
+
+
+CSubmissionPut::CSubmissionPut(std::u16string_view aURL, const css::uno::Reference< css::xml::dom::XDocumentFragment >& aFragment)
+ : CSubmission(aURL, aFragment)
+{
+}
+
+CSubmission::SubmissionResult CSubmissionPut::submit(const css::uno::Reference< css::task::XInteractionHandler >& aInteractionHandler)
+{
+ css::uno::Reference< XCommandEnvironment > aEnvironment;
+ std::unique_ptr< CSerialization > apSerialization(createSerialization(aInteractionHandler,aEnvironment));
+
+ try {
+ ucbhelper::Content aContent(m_aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aEnvironment, comphelper::getProcessComponentContext());
+
+ // insert serialized data to content -> PUT
+ css::uno::Reference< XInputStream > aInStream = apSerialization->getInputStream();
+ aContent.writeStream(aInStream, true);
+ //aContent.closeStream();
+
+ // no content as a result of put...
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "forms.misc", "Exception during UCB operation." );
+ return UNKNOWN_ERROR;
+ }
+
+
+ return SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/submission/submission_put.hxx b/forms/source/xforms/submission/submission_put.hxx
new file mode 100644
index 000000000..2dce84307
--- /dev/null
+++ b/forms/source/xforms/submission/submission_put.hxx
@@ -0,0 +1,33 @@
+/* -*- 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 "submission.hxx"
+
+class CSubmissionPut : public CSubmission
+{
+public:
+ CSubmissionPut(std::u16string_view aURL,
+ const css::uno::Reference<css::xml::dom::XDocumentFragment>& aFragment);
+ virtual SubmissionResult
+ submit(const css::uno::Reference<css::task::XInteractionHandler>& aInteractionHandler) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/unohelper.cxx b/forms/source/xforms/unohelper.cxx
new file mode 100644
index 000000000..8576e5e29
--- /dev/null
+++ b/forms/source/xforms/unohelper.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 "unohelper.hxx"
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Exception;
+using com::sun::star::beans::Property;
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::beans::XPropertySetInfo;
+using com::sun::star::beans::PropertyAttribute::READONLY;
+
+void xforms::copy(const Reference<XPropertySet>& xFrom, Reference<XPropertySet> const& xTo)
+{
+ OSL_ENSURE(xFrom.is(), "no source");
+ OSL_ENSURE(xTo.is(), "no target");
+
+ // get property names & infos, and iterate over target properties
+ Sequence<Property> aProperties = xTo->getPropertySetInfo()->getProperties();
+ sal_Int32 nProperties = aProperties.getLength();
+ const Property* pProperties = aProperties.getConstArray();
+ Reference<XPropertySetInfo> xFromInfo = xFrom->getPropertySetInfo();
+ for (sal_Int32 n = 0; n < nProperties; n++)
+ {
+ const OUString& rName = pProperties[n].Name;
+
+ // if both set have the property, copy the value
+ // (catch and ignore exceptions, if any)
+ if (xFromInfo->hasPropertyByName(rName))
+ {
+ try
+ {
+ Property aProperty = xFromInfo->getPropertyByName(rName);
+ if ((aProperty.Attributes & READONLY) == 0)
+ xTo->setPropertyValue(rName, xFrom->getPropertyValue(rName));
+ }
+ catch (const Exception&)
+ {
+ // ignore any errors; we'll copy as good as we can
+ }
+ }
+ // else: no property? then ignore.
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/unohelper.hxx b/forms/source/xforms/unohelper.hxx
new file mode 100644
index 000000000..5aace2f3c
--- /dev/null
+++ b/forms/source/xforms/unohelper.hxx
@@ -0,0 +1,41 @@
+/* -*- 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/types.h>
+
+namespace com::sun::star {
+ namespace uno {
+ class XInterface;
+ template<class T> class Reference;
+ }
+ namespace beans {
+ class XPropertySet;
+ }
+}
+
+namespace xforms
+{
+
+/** copy the properties from one PropertySet into the next */
+void copy( const css::uno::Reference<css::beans::XPropertySet>& , css::uno::Reference<css::beans::XPropertySet> const & );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xforms_services.cxx b/forms/source/xforms/xforms_services.cxx
new file mode 100644
index 000000000..23a1ee400
--- /dev/null
+++ b/forms/source/xforms/xforms_services.cxx
@@ -0,0 +1,59 @@
+/* -*- 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 "NameContainer.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class Implementation:
+ public cppu::ImplInheritanceHelper<
+ NameContainer<css::uno::Reference<css::beans::XPropertySet>>,
+ css::lang::XServiceInfo>
+{
+ OUString SAL_CALL getImplementationName() override
+ { return "com.sun.star.form.XForms"; }
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return {"com.sun.star.xforms.XForms"};
+ }
+};
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_form_XForms_get_implementation(uno::XComponentContext*,
+ uno::Sequence<uno::Any> const &)
+{
+ return cppu::acquire(new Implementation);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xformsevent.cxx b/forms/source/xforms/xformsevent.cxx
new file mode 100644
index 000000000..fb9758f1f
--- /dev/null
+++ b/forms/source/xforms/xformsevent.cxx
@@ -0,0 +1,83 @@
+/* -*- 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 "xformsevent.hxx"
+
+namespace com::sun::star::xforms {
+
+void SAL_CALL XFormsEventConcrete::initXFormsEvent(const OUString& typeArg,
+ sal_Bool canBubbleArg, sal_Bool cancelableArg)
+{
+ initEvent(typeArg, canBubbleArg, cancelableArg);
+}
+
+OUString SAL_CALL XFormsEventConcrete::getType()
+{
+ return m_eventType;
+}
+
+css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL XFormsEventConcrete::getTarget()
+{
+ return css::uno::Reference< css::xml::dom::events::XEventTarget >();
+}
+
+css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL XFormsEventConcrete::getCurrentTarget()
+{
+ return css::uno::Reference< css::xml::dom::events::XEventTarget >();
+}
+
+css::xml::dom::events::PhaseType SAL_CALL XFormsEventConcrete::getEventPhase()
+{
+ return css::xml::dom::events::PhaseType_CAPTURING_PHASE;
+}
+
+sal_Bool SAL_CALL XFormsEventConcrete::getBubbles()
+{
+ return m_bubbles;
+}
+
+sal_Bool SAL_CALL XFormsEventConcrete::getCancelable()
+{
+ return m_cancelable;
+}
+
+css::util::Time SAL_CALL XFormsEventConcrete::getTimeStamp()
+{
+ return css::util::Time();
+}
+
+void SAL_CALL XFormsEventConcrete::stopPropagation()
+{
+}
+void SAL_CALL XFormsEventConcrete::preventDefault()
+{
+}
+
+void SAL_CALL XFormsEventConcrete::initEvent(const OUString& eventTypeArg, sal_Bool canBubbleArg,
+ sal_Bool cancelableArg)
+{
+ m_eventType = eventTypeArg;
+ m_bubbles = canBubbleArg;
+ m_cancelable = cancelableArg;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xformsevent.hxx b/forms/source/xforms/xformsevent.hxx
new file mode 100644
index 000000000..2622780be
--- /dev/null
+++ b/forms/source/xforms/xformsevent.hxx
@@ -0,0 +1,68 @@
+/* -*- 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/types.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/xforms/XFormsEvent.hpp>
+#include <com/sun/star/xml/dom/events/XEventTarget.hpp>
+
+namespace com::sun::star::xforms {
+
+class XFormsEventConcrete : public cppu::WeakImplHelper< XFormsEvent > {
+
+ public:
+
+ XFormsEventConcrete()
+ : m_bubbles(false)
+ , m_cancelable(false)
+ {
+ }
+
+ virtual OUString SAL_CALL getType() override;
+ virtual css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL getTarget() override;
+ virtual css::uno::Reference< css::xml::dom::events::XEventTarget > SAL_CALL getCurrentTarget() override;
+ virtual css::xml::dom::events::PhaseType SAL_CALL getEventPhase() override;
+ virtual sal_Bool SAL_CALL getBubbles() override;
+ virtual sal_Bool SAL_CALL getCancelable() override;
+ virtual css::util::Time SAL_CALL getTimeStamp() override;
+ virtual void SAL_CALL stopPropagation() override;
+ virtual void SAL_CALL preventDefault() override;
+
+ virtual void SAL_CALL initXFormsEvent(
+ const OUString& typeArg,
+ sal_Bool canBubbleArg,
+ sal_Bool cancelableArg ) override;
+
+ virtual void SAL_CALL initEvent(
+ const OUString& eventTypeArg,
+ sal_Bool canBubbleArg,
+ sal_Bool cancelableArg) override;
+
+ private:
+
+ OUString m_eventType;
+ bool m_bubbles;
+ bool m_cancelable;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xmlhelper.cxx b/forms/source/xforms/xmlhelper.cxx
new file mode 100644
index 000000000..75f0f92bd
--- /dev/null
+++ b/forms/source/xforms/xmlhelper.cxx
@@ -0,0 +1,133 @@
+/* -*- 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 "xmlhelper.hxx"
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <comphelper/processfactory.hxx>
+
+using com::sun::star::uno::Reference;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::xml::dom::DocumentBuilder;
+using com::sun::star::xml::dom::XDocumentBuilder;
+
+
+// determine valid XML name
+
+
+// character class:
+// 1: NameStartChar
+// 2: NameChar
+// 4: NCNameStartChar
+// 8: NCNameChar
+static sal_uInt8 lcl_getCharClass( sal_Unicode c )
+{
+ sal_uInt8 nClass = 0;
+
+ // NameStartChar
+ if( (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= 0x00C0 && c <= 0x00D6)
+ || (c >= 0x00D8 && c <= 0x00F6)
+ || (c >= 0x00F8 && c <= 0x02FF)
+ || (c >= 0x0370 && c <= 0x037D)
+ || (c >= 0x037F && c <= 0x1FFF)
+ || (c >= 0x200C && c <= 0x200D)
+ || (c >= 0x2070 && c <= 0x218F)
+ || (c >= 0x2C00 && c <= 0x2FEF)
+ || (c >= 0x3001 && c <= 0xD7FF)
+ || (c >= 0xF900 && c <= 0xFDCF)
+ || (c >= 0xFDF0 && c <= 0xFFFD)
+
+ // surrogates
+ || (c >= 0xD800 && c <= 0xDBFF)
+ || (c >= 0xDC00 && c <= 0xDFFF) )
+ {
+ nClass = 15;
+ }
+ else if( c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || (c == 0x00B7)
+ || (c >= 0x0300 && c <= 0x036F)
+ || (c >= 0x203F && c <= 0x2040) )
+ {
+ nClass = 10;
+ }
+ else if( c == ':' )
+ {
+ nClass = 3;
+ }
+
+ return nClass;
+}
+
+bool isValidQName( const OUString& sName,
+ const Reference<XNameContainer>& /*xNamespaces*/ )
+{
+ sal_Int32 nLength = sName.getLength();
+ const sal_Unicode* pName = sName.getStr();
+
+ bool bRet = false;
+ sal_Int32 nColon = 0;
+ if( nLength > 0 )
+ {
+ bRet = ( ( lcl_getCharClass( pName[0] ) & 4 ) != 0 );
+ for( sal_Int32 n = 1; n < nLength; n++ )
+ {
+ sal_uInt8 nClass = lcl_getCharClass( pName[n] );
+ bRet &= ( ( nClass & 2 ) != 0 );
+ if( nClass == 3 )
+ nColon++;
+ }
+ }
+ if( nColon > 1 )
+ bRet = false;
+
+ return bRet;
+}
+
+bool isValidPrefixName( const OUString& sName,
+ const Reference<XNameContainer>& /*xNamespaces*/ )
+{
+ sal_Int32 nLength = sName.getLength();
+ const sal_Unicode* pName = sName.getStr();
+ bool bRet = false;
+
+ if( nLength > 0 )
+ {
+ bRet = ( ( lcl_getCharClass( pName[0] ) & 4 ) != 0 );
+ for( sal_Int32 n = 1; n < nLength; n++ )
+ bRet &= ( ( lcl_getCharClass( pName[n] ) & 8 ) != 0 );
+ }
+
+ return bRet;
+}
+
+Reference<XDocumentBuilder> getDocumentBuilder()
+{
+ Reference<XDocumentBuilder> xBuilder(DocumentBuilder::create(::comphelper::getProcessComponentContext()));
+ return xBuilder;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xmlhelper.hxx b/forms/source/xforms/xmlhelper.hxx
new file mode 100644
index 000000000..e8995f764
--- /dev/null
+++ b/forms/source/xforms/xmlhelper.hxx
@@ -0,0 +1,40 @@
+/* -*- 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>
+
+namespace com::sun::star {
+ namespace uno { template<typename T> class Reference; }
+ namespace container { class XNameContainer; }
+ namespace xml::dom { class XDocumentBuilder; }
+}
+
+
+bool isValidQName( const OUString& sName,
+ const css::uno::Reference<css::container::XNameContainer>& xNamespaces );
+
+bool isValidPrefixName( const OUString& sName,
+ const css::uno::Reference<css::container::XNameContainer>& xNamespaces );
+
+css::uno::Reference<css::xml::dom::XDocumentBuilder> getDocumentBuilder();
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/extension.cxx b/forms/source/xforms/xpathlib/extension.cxx
new file mode 100644
index 000000000..0af6a3eb1
--- /dev/null
+++ b/forms/source/xforms/xpathlib/extension.cxx
@@ -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 .
+ */
+
+
+#include <com/sun/star/xml/xpath/Libxml2ExtensionHandle.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include "extension.hxx"
+#include "xpathlib.hxx"
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xforms;
+using namespace com::sun::star::xml::xpath;
+using namespace com::sun::star::beans;
+
+Libxml2ExtensionHandle SAL_CALL CLibxml2XFormsExtension::getLibxml2ExtensionHandle()
+{
+ Libxml2ExtensionHandle aHandle;
+ aHandle.functionLookupFunction = reinterpret_cast< sal_Int64 >( &xforms_lookupFunc );
+ aHandle.functionData = reinterpret_cast< sal_Int64 >( this );
+ aHandle.variableLookupFunction = sal_Int64(0);
+ aHandle.variableData = sal_Int64(0);
+ return aHandle;
+}
+
+void SAL_CALL CLibxml2XFormsExtension::initialize(const Sequence< Any >& aSequence)
+{
+ if (aSequence.getLength() == 2
+ && (aSequence[0] >>= m_aModel)
+ && (aSequence[1] >>= m_aContextNode))
+ {
+ return;
+ }
+
+ NamedValue aValue;
+ for (const Any& rArg : aSequence)
+ {
+ if (! (rArg >>= aValue))
+ throw RuntimeException();
+ if ( aValue.Name == "Model" )
+ aValue.Value >>= m_aModel;
+ else if ( aValue.Name == "ContextNode" )
+ aValue.Value >>= m_aContextNode;
+ }
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_xml_xpath_XFormsExtension_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new CLibxml2XFormsExtension());
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/extension.hxx b/forms/source/xforms/xpathlib/extension.hxx
new file mode 100644
index 000000000..267576b74
--- /dev/null
+++ b/forms/source/xforms/xpathlib/extension.hxx
@@ -0,0 +1,52 @@
+/* -*- 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/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/xml/xpath/XXPathExtension.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+
+class CLibxml2XFormsExtension
+ : public cppu::WeakImplHelper<css::xml::xpath::XXPathExtension, css::lang::XInitialization>
+{
+private:
+ css::uno::Reference<css::xforms::XModel> m_aModel;
+ css::uno::Reference<css::xml::dom::XNode> m_aContextNode;
+
+public:
+ CLibxml2XFormsExtension() {}
+
+ const css::uno::Reference<css::xforms::XModel>& getModel() const { return m_aModel; }
+ const css::uno::Reference<css::xml::dom::XNode>& getContextNode() const
+ {
+ return m_aContextNode;
+ }
+
+ virtual css::xml::xpath::Libxml2ExtensionHandle SAL_CALL getLibxml2ExtensionHandle() override;
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& aSequence) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/xpathlib.cxx b/forms/source/xforms/xpathlib/xpathlib.cxx
new file mode 100644
index 000000000..e2f0a10eb
--- /dev/null
+++ b/forms/source/xforms/xpathlib/xpathlib.cxx
@@ -0,0 +1,539 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <string.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/string_view.hxx>
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <tools/datetime.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+#include <boost/lexical_cast.hpp>
+#include <libxml/xpathInternals.h>
+
+#include "xpathlib.hxx"
+#include "extension.hxx"
+
+// C interface
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::xml::dom;
+using namespace com::sun::star::xforms;
+using namespace com::sun::star::lang;
+
+xmlXPathFunction xforms_lookupFunc(void *, const xmlChar *xname, const xmlChar *)
+{
+
+ const char *name = reinterpret_cast<char const *>(xname);
+ if (strcmp("boolean-from-string", name)==0)
+ return xforms_booleanFromStringFunction;
+ else if ((strcmp("if", name))==0)
+ return xforms_ifFunction;
+ else if ((strcmp("avg", name))==0)
+ return xforms_avgFunction;
+ else if ((strcmp("min", name))==0)
+ return xforms_minFunction;
+ else if ((strcmp("max", name))==0)
+ return xforms_maxFunction;
+ else if ((strcmp("count-non-empty", name))==0)
+ return xforms_countNonEmptyFunction;
+ else if ((strcmp("index", name))==0)
+ return xforms_indexFunction;
+ else if ((strcmp("property", name))==0)
+ return xforms_propertyFunction;
+ else if ((strcmp("now", name))==0)
+ return xforms_nowFunction;
+ else if ((strcmp("days-from-date", name))==0)
+ return xforms_daysFromDateFunction;
+ else if ((strcmp("seconds-from-dateTime", name))==0)
+ return xforms_secondsFromDateTimeFunction;
+ else if ((strcmp("seconds", name))==0)
+ return xforms_secondsFunction;
+ else if ((strcmp("months", name))==0)
+ return xforms_monthsFunction;
+ else if ((strcmp("instance", name))==0)
+ return xforms_instanceFunction;
+ else if ((strcmp("current", name))==0)
+ return xforms_currentFunction;
+ else
+ return nullptr;
+}
+
+// boolean functions
+void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar *pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+ if (aString.equalsIgnoreAsciiCase("true") ||
+ aString.equalsIgnoreAsciiCase("1"))
+ xmlXPathReturnTrue(ctxt);
+ else if (aString.equalsIgnoreAsciiCase("false") ||
+ aString.equalsIgnoreAsciiCase("0"))
+ xmlXPathReturnFalse(ctxt);
+ else
+ XP_ERROR(XPATH_NUMBER_ERROR);
+}
+
+void xforms_ifFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 3) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar *s2 = xmlXPathPopString(ctxt);
+
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ xmlChar *s1 = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ bool aBool = xmlXPathPopBoolean(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+
+ if (aBool)
+ xmlXPathReturnString(ctxt, s1);
+ else
+ xmlXPathReturnString(ctxt, s2);
+
+}
+
+// Number Functions
+void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // use sum(), div() and count()
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+
+ // save nodeset
+ xmlXPathObjectPtr pObject = valuePop(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ //push back a copy
+ valuePush(ctxt, xmlXPathObjectCopy(pObject));
+ // get the Sum
+ xmlXPathSumFunction(ctxt, 1);
+ double nSum = xmlXPathPopNumber(ctxt);
+ // push a copy once more
+ valuePush(ctxt, xmlXPathObjectCopy(pObject));
+ xmlXPathCountFunction(ctxt, 1);
+ double nCount = xmlXPathPopNumber(ctxt);
+ // push args for div()
+ xmlXPathReturnNumber(ctxt, nSum);
+ xmlXPathReturnNumber(ctxt, nCount);
+ xmlXPathDivValues(ctxt);
+ // the result is now on the ctxt stack
+ xmlXPathFreeObject(pObject);
+}
+
+void xforms_minFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ double nMinimum = 0;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
+ {
+ double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
+ if (xmlXPathIsNaN(nNumber))
+ {
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+ return;
+ }
+ if (i == 0)
+ nMinimum = nNumber;
+ else if (nNumber < nMinimum)
+ nMinimum = nNumber;
+ }
+ xmlXPathReturnNumber(ctxt, nMinimum);
+}
+
+void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ double nMaximum = 0;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
+ {
+ double nNumber = xmlXPathCastNodeToNumber(xmlXPathNodeSetItem(pNodeSet, i));
+ if (xmlXPathIsNaN(nNumber))
+ {
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+ return;
+ }
+ if (i == 0)
+ nMaximum = nNumber;
+ else if (nNumber > nMaximum)
+ nMaximum = nNumber;
+ }
+ xmlXPathReturnNumber(ctxt, nMaximum);
+}
+void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlNodeSetPtr pNodeSet = xmlXPathPopNodeSet(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ sal_Int32 nNotEmpty = 0;
+ for (int i = 0; i < xmlXPathNodeSetGetLength(pNodeSet); i++)
+ {
+ const xmlChar *aString = xmlXPathCastNodeToString(xmlXPathNodeSetItem(pNodeSet, i));
+ if (*aString != 0) nNotEmpty++;
+ }
+ xmlXPathReturnNumber(ctxt, nNotEmpty);
+}
+void xforms_indexFunction(xmlXPathParserContextPtr /*ctxt*/, int /*nargs*/)
+{
+ // function index takes a string argument that is the IDREF of a
+ // 'repeat' and returns the current 1-based position of the repeat
+ // index of the identified repeat -- see xforms/9.3.1
+
+ // doc.getElementByID
+ // (...)
+}
+
+// String Functions
+void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+ if (aString.equalsIgnoreAsciiCase("version"))
+ xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>("1.0")));
+ else if (aString.equalsIgnoreAsciiCase("conformance-level"))
+ xmlXPathReturnString(ctxt, reinterpret_cast<xmlChar *>(const_cast<char *>("conformance")));
+ else
+ xmlXPathReturnEmptyString(ctxt);
+}
+
+// Date and Time Functions
+
+static OString makeDateTimeString (const DateTime& aDateTime)
+{
+ OStringBuffer aDateTimeString;
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetYear()));
+ aDateTimeString.append('-');
+ if (aDateTime.GetMonth()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
+ aDateTimeString.append('-');
+ if (aDateTime.GetDay()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetDay()));
+ aDateTimeString.append('T');
+ if (aDateTime.GetHour()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetHour()));
+ aDateTimeString.append(':');
+ if (aDateTime.GetMin()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetMin()));
+ aDateTimeString.append(':');
+ if (aDateTime.GetSec()<10) aDateTimeString.append('0');
+ aDateTimeString.append(static_cast<sal_Int32>(aDateTime.GetSec()));
+ aDateTimeString.append('Z');
+
+ return aDateTimeString.makeStringAndClear();
+}
+
+// returns current system date and time in canonical xsd:dateTime
+// format
+void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int /*nargs*/)
+{
+ /*
+ A single lexical representation, which is a subset of the lexical representations
+ allowed by [ISO 8601], is allowed for dateTime. This lexical representation is the
+ [ISO 8601] extended format CCYY-MM-DDThh:mm:ss where "CC" represents the century,
+ "YY" the year, "MM" the month and "DD" the day, preceded by an optional leading "-"
+ sign to indicate a negative number. If the sign is omitted, "+" is assumed. The letter
+ "T" is the date/time separator and "hh", "mm", "ss" represent hour, minute and second
+ respectively.
+ */
+
+ /*
+ 3.2.7.2 Canonical representation
+ The canonical representation for dateTime is defined by prohibiting certain options
+ from the Lexical representation (par.3.2.7.1). Specifically, either the time zone must
+ be omitted or, if present, the time zone must be Coordinated Universal tools::Time (UTC)
+ indicated by a "Z".
+ */
+ DateTime aDateTime( DateTime::SYSTEM );
+ OString aDateTimeString = makeDateTimeString(aDateTime);
+ xmlChar *pString = static_cast<xmlChar*>(xmlMalloc(aDateTimeString.getLength()+1));
+ strncpy(reinterpret_cast<char*>(pString), aDateTimeString.getStr(), aDateTimeString.getLength());
+ pString[aDateTimeString.getLength()] = 0;
+ xmlXPathReturnString(ctxt, pString);
+}
+
+static bool parseDateTime(std::u16string_view aString, DateTime& aDateTime)
+{
+ // take apart a canonical literal xsd:dateTime string
+ //CCYY-MM-DDThh:mm:ss(Z)
+
+ OUString aDateTimeString( o3tl::trim(aString) );
+
+ // check length
+ if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20)
+ return false;
+
+ sal_Int32 nIndex = 0;
+ sal_Int32 nYear = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, '-', nIndex));
+ sal_Int32 nMonth = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, '-', nIndex));
+ sal_Int32 nDay = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, 'T', nIndex));
+ sal_Int32 nHour = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, ':', nIndex));
+ sal_Int32 nMinute = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, ':', nIndex));
+ sal_Int32 nSecond = o3tl::toInt32(o3tl::getToken(aDateTimeString, 0, 'Z', nIndex));
+
+ Date tmpDate(static_cast<sal_uInt16>(nDay), static_cast<sal_uInt16>(nMonth), static_cast<sal_uInt16>(nYear));
+ tools::Time tmpTime(nHour, nMinute, nSecond);
+ DateTime tmpDateTime(tmpDate, tmpTime);
+ if (aString.rfind('Z') == std::u16string_view::npos)
+ tmpDateTime.ConvertToUTC();
+
+ aDateTime = tmpDateTime;
+
+ return true;
+}
+
+
+void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // number of days from 1970-01-01 to supplied xsd:date(Time)
+
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+
+ DateTime aDateTime( DateTime::EMPTY );
+ if (parseDateTime(aString, aDateTime))
+ {
+ Date aReferenceDate(1, 1, 1970);
+ sal_Int32 nDays = aDateTime - aReferenceDate;
+ xmlXPathReturnNumber(ctxt, nDays);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+
+
+}
+
+
+void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // number of seconds from 1970-01-01T00:00:00Z to supplied xsd:date(Time)
+
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+
+ DateTime aDateTime( DateTime::EMPTY );
+
+ if (parseDateTime(aString, aDateTime))
+ {
+ Date aReferenceDate(1, 1, 1970);
+ sal_Int32 nDays = aDateTime - aReferenceDate;
+ sal_Int32 nSeconds = nDays * 24 * 60 * 60;
+ nSeconds += aDateTime.GetHour() * 60 * 60;
+ nSeconds += aDateTime.GetMin() * 60;
+ nSeconds += aDateTime.GetSec();
+ xmlXPathReturnNumber(ctxt, nSeconds);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+
+}
+
+static bool parseDuration(const xmlChar* aString, bool& bNegative, sal_Int32& nYears, sal_Int32& nMonth, sal_Int32& nDays,
+ sal_Int32& nHours, sal_Int32& nMinutes, sal_Int32& nSeconds)
+{
+ bool bTime = false; // in part after T
+ const xmlChar *pString = aString;
+
+ if (pString[0] == '-') {
+ bNegative = true;
+ pString++;
+ }
+
+ if (pString[0] != 'P')
+ {
+ return false;
+ }
+
+ pString++;
+ const xmlChar* pToken = pString;
+ while(pToken[0] != 0)
+ {
+ switch(pToken[0]) {
+ case 'Y':
+ nYears = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'M':
+ if (!bTime)
+ nMonth = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ else
+ nMinutes = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'D':
+ nDays = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'H':
+ nHours = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'S':
+ nSeconds = boost::lexical_cast<sal_Int32>(pString, pString-pToken);
+ pString = ++pToken;
+ break;
+ case 'T':
+ bTime = true;
+ pString = ++pToken;
+ break;
+ default:
+ pToken++;
+ }
+ }
+ return true;
+}
+
+void xforms_secondsFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // convert a xsd:duration to seconds
+ // (-)PnYnMnDTnHnMnS
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+
+ bool bNegative = false;
+ sal_Int32 nYears = 0;
+ sal_Int32 nMonths = 0;
+ sal_Int32 nDays = 0;
+ sal_Int32 nHours = 0;
+ sal_Int32 nMinutes = 0;
+ sal_Int32 nSeconds = 0;
+
+ if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
+ {
+ nSeconds += nMinutes*60;
+ nSeconds += nHours*60*60;
+ nSeconds += nDays*24*60*60;
+ // year and month are ignored according to spec
+ if (bNegative)
+ nSeconds = 0 - nSeconds;
+ xmlXPathReturnNumber(ctxt, nSeconds);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+}
+
+void xforms_monthsFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ // convert a xsd:duration to seconds
+ // (-)PnYnMnDTnHnMnS
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar* pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+
+ bool bNegative = false;
+ sal_Int32 nYears = 0;
+ sal_Int32 nMonths = 0;
+ sal_Int32 nDays = 0;
+ sal_Int32 nHours = 0;
+ sal_Int32 nMinutes = 0;
+ sal_Int32 nSeconds = 0;
+
+ if (parseDuration(pString, bNegative, nYears, nMonths, nDays, nHours, nMinutes, nSeconds))
+ {
+ nMonths += nYears*12;
+ // Days, Hours, Minutes and seconds are ignored, see spec
+ if (bNegative)
+ nMonths = 0 - nMonths;
+ xmlXPathReturnNumber(ctxt, nMonths);
+ }
+ else
+ xmlXPathReturnNumber(ctxt, xmlXPathNAN);
+
+}
+
+// Node-set Functions
+void xforms_instanceFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 1) XP_ERROR(XPATH_INVALID_ARITY);
+ xmlChar *pString = xmlXPathPopString(ctxt);
+ if (xmlXPathCheckError(ctxt)) XP_ERROR(XPATH_INVALID_TYPE);
+ OUString aString(reinterpret_cast<char*>(pString), strlen(reinterpret_cast<char*>(pString)), RTL_TEXTENCODING_UTF8);
+
+ Reference< XModel > aModel = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getModel();
+ if (aModel.is())
+ {
+ Reference< XDocument > aInstance = aModel->getInstanceDocument(aString);
+ if (aInstance.is())
+ {
+ try {
+ // xmlXPathObjectPtr xmlXPathNewNodeSet (xmlNodePtr val);
+ Reference< XUnoTunnel > aTunnel(aInstance, UNO_QUERY_THROW);
+ xmlNodePtr pNode = comphelper::getSomething_cast<xmlNode>(aTunnel->getSomething(Sequence<sal_Int8>()));
+ xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
+ xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
+ } catch (const RuntimeException&)
+ {
+ xmlXPathReturnEmptyNodeSet(ctxt);
+ }
+ }
+ else
+ xmlXPathReturnEmptyNodeSet(ctxt);
+ }
+ else
+ xmlXPathReturnEmptyNodeSet(ctxt);
+
+}
+
+// Node-set Functions, XForms 1.1
+void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+ if (nargs != 0) XP_ERROR(XPATH_INVALID_ARITY);
+
+ Reference< XNode > aNode = static_cast<CLibxml2XFormsExtension*>(ctxt->context->funcLookupData)->getContextNode();
+
+ if (aNode.is())
+ {
+ try {
+ Reference< XUnoTunnel > aTunnel(aNode, UNO_QUERY_THROW);
+ xmlNodePtr pNode = comphelper::getSomething_cast<xmlNode>(aTunnel->getSomething(Sequence<sal_Int8>()));
+ xmlXPathObjectPtr pObject = xmlXPathNewNodeSet(pNode);
+ xmlXPathReturnNodeSet(ctxt, pObject->nodesetval);
+ }
+ catch (const RuntimeException&)
+ {
+ xmlXPathReturnEmptyNodeSet(ctxt);
+ }
+ }
+ else
+ xmlXPathReturnEmptyNodeSet(ctxt);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/forms/source/xforms/xpathlib/xpathlib.hxx b/forms/source/xforms/xpathlib/xpathlib.hxx
new file mode 100644
index 000000000..52a477c97
--- /dev/null
+++ b/forms/source/xforms/xpathlib/xpathlib.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 <libxml/xpath.h>
+
+/*
+ entry functions for libxml xpath engine
+
+*/
+
+/**
+ * xmlXPathParserContext:
+ *
+ * An XPath parser context. It contains pure parsing information,
+ * an xmlXPathContext, and the stack of objects.
+ */
+
+extern "C"
+{
+
+// XForms
+
+// XForms Core Functions
+// boolean functions
+void xforms_booleanFromStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_ifFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Number Functions
+void xforms_avgFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_minFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_maxFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_countNonEmptyFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_indexFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// String Functions
+void xforms_propertyFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Date and Time Functions
+void xforms_nowFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_daysFromDateFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_secondsFromDateTimeFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_secondsFunction(xmlXPathParserContextPtr ctxt, int nargs);
+void xforms_monthsFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Node-set Functions
+void xforms_instanceFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// Node-set Functions; XForms 1.1
+void xforms_currentFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+// --- lookup ---
+xmlXPathFunction xforms_lookupFunc(void *ctxt, const xmlChar *name, const xmlChar *ns_uri);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */