blob: faf4f8a93c2c0274468f93121ea7d5c19be5439a [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_forms.hxx"
#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 <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <tools/debug.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/XInputStream.hpp>
#include <com/sun/star/io/XActiveDataSink.hpp>
#include <com/sun/star/io/XTextInputStream.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 rtl::OUString;
using rtl::OUStringBuffer;
using com::sun::star::beans::PropertyValue;
using com::sun::star::io::XInputStream;
using com::sun::star::io::XActiveDataSink;
using com::sun::star::io::XTextInputStream;
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 XNode_t& xNode )
throw( RuntimeException )
{
// determine service for control. string/text field is default.
OUString sService = OUSTRING("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 com::sun::star::xsd::DataTypeClass::BOOLEAN:
sService = OUSTRING("com.sun.star.form.component.CheckBox");
break;
case com::sun::star::xsd::DataTypeClass::DOUBLE:
case com::sun::star::xsd::DataTypeClass::DECIMAL:
case com::sun::star::xsd::DataTypeClass::FLOAT:
sService = OUSTRING("com.sun.star.form.component.NumericField");
break;
case com::sun::star::xsd::DataTypeClass::STRING:
case com::sun::star::xsd::DataTypeClass::DURATION:
case com::sun::star::xsd::DataTypeClass::DATETIME:
case com::sun::star::xsd::DataTypeClass::TIME:
case com::sun::star::xsd::DataTypeClass::DATE:
case com::sun::star::xsd::DataTypeClass::gYearMonth:
case com::sun::star::xsd::DataTypeClass::gYear:
case com::sun::star::xsd::DataTypeClass::gMonthDay:
case com::sun::star::xsd::DataTypeClass::gDay:
case com::sun::star::xsd::DataTypeClass::gMonth:
case com::sun::star::xsd::DataTypeClass::hexBinary:
case com::sun::star::xsd::DataTypeClass::base64Binary:
case com::sun::star::xsd::DataTypeClass::anyURI:
case com::sun::star::xsd::DataTypeClass::QName:
case com::sun::star::xsd::DataTypeClass::NOTATION:
default:
// keep default
break;
}
}
return sService;
}
void lcl_OutPosition( OUStringBuffer& rBuffer,
const Reference<XNode>& xNode )
{
OSL_ENSURE( xNode->getParentNode().is(), "need parent" );
// count # of occurences of this node
sal_Int32 nFound = 0;
sal_Int32 nPosition = -1;
if( xNode->getParentNode().is() )
{
for( Reference<XNode> xIter = xNode->getParentNode()->getFirstChild();
xIter != NULL;
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, sal_Unicode(']') );
rBuffer.insert( 0, nPosition );
rBuffer.insert( 0, sal_Unicode('[') );
}
}
void lcl_OutName( OUStringBuffer& rBuffer,
const Reference<XNode>& xNode )
{
rBuffer.insert( 0, xNode->getNodeName() );
OUString sPrefix = xNode->getPrefix();
if( sPrefix.getLength() > 0 )
{
rBuffer.insert( 0, sal_Unicode(':') );
rBuffer.insert( 0, sPrefix );
}
}
void lcl_OutInstance( OUStringBuffer& rBuffer,
const Reference<XNode>& xNode,
Model* pModel )
{
Reference<XDocument> xDoc = xNode->getOwnerDocument();
if( xDoc != pModel->getDefaultInstance() )
{
rBuffer.insert( 0, OUSTRING("')") );
// iterate over instances, and find the right one
OUString sInstanceName;
Reference<XEnumeration> xEnum =
pModel->getInstances()->createEnumeration();
while( ( sInstanceName.getLength() == 0 ) && xEnum->hasMoreElements() )
{
Sequence<PropertyValue> aValues;
xEnum->nextElement() >>= aValues;
// get ID and instance
OUString sId;
Reference<XDocument> xInstance;
getInstanceData( aValues, &sId, &xInstance, NULL, NULL );
// now check whether this was our instance:
if( xInstance == xDoc )
sInstanceName = sId;
}
rBuffer.insert( 0, sInstanceName );
rBuffer.insert( 0, OUSTRING("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.getLength() > 0 )
aBuffer.insert( 0, sal_Unicode('/') );
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, OUSTRING("text()") );
break;
case NodeType_ATTRIBUTE_NODE:
lcl_OutName( aBuffer, xCurrent );
aBuffer.insert( 0, sal_Unicode('@') );
break;
case NodeType_DOCUMENT_NODE:
// check for which instance we have
lcl_OutInstance( aBuffer, xCurrent, this );
break;
default:
// unknown type? fail!
OSL_ENSURE( false, "unknown node type!" );
xCurrent.set( NULL );
aBuffer.makeStringAndClear();
// we'll remove the slash below
aBuffer.insert( 0, sal_Unicode('/') );
break;
}
}
return aBuffer.makeStringAndClear();
}
OUString Model::getDefaultBindingExpressionForNode( const XNode_t& xNode )
throw( RuntimeException )
{
return getDefaultBindingExpressionForNode( xNode, getEvaluationContext() );
}
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 == sal_Unicode(0x09) ||
c == sal_Unicode(0x0A) ||
c == sal_Unicode(0x0D) ||
c == sal_Unicode(0x20) );
}
return bWhitespace;
}
OUString Model::getNodeDisplayName( const XNode_t& xNode,
sal_Bool bDetail )
throw( RuntimeException )
{
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( sal_Unicode('"') );
aBuffer.append( Convert::collapseWhitespace( sContent ) );
aBuffer.append( sal_Unicode('"') );
}
}
break;
case NodeType_ATTRIBUTE_NODE:
lcl_OutName( aBuffer, xNode );
aBuffer.insert( 0, sal_Unicode('@') );
break;
case NodeType_DOCUMENT_NODE:
if( xNode == getDefaultInstance() )
aBuffer.append( sal_Unicode('/') );
else
lcl_OutInstance( aBuffer, xNode, this );
break;
default:
// unknown type? fail!
OSL_ENSURE( false, "unknown node type!" );
break;
}
return aBuffer.makeStringAndClear();
}
OUString Model::getNodeName( const XNode_t& xNode )
throw( RuntimeException )
{
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_ENSURE( false, "no name for this node type!" );
break;
}
return aBuffer.makeStringAndClear();
}
OUString Model::getBindingName( const XPropertySet_t& xBinding,
sal_Bool /*bDetail*/ )
throw( RuntimeException )
{
OUString sID;
xBinding->getPropertyValue( OUSTRING("BindingID" ) ) >>= sID;
OUString sExpression;
xBinding->getPropertyValue( OUSTRING("BindingExpression" ) ) >>= sExpression;
OUStringBuffer aBuffer;
if( sID.getLength() > 0 )
{
aBuffer.append( sID );
aBuffer.append( OUSTRING(" (" ));
aBuffer.append( sExpression );
aBuffer.append( OUSTRING(")" ));
}
else
aBuffer.append( sExpression );
return aBuffer.makeStringAndClear();
}
OUString Model::getSubmissionName( const XPropertySet_t& xSubmission,
sal_Bool /*bDetail*/ )
throw( RuntimeException )
{
OUString sID;
xSubmission->getPropertyValue( OUSTRING("ID") ) >>= sID;
return sID;
}
Model::XPropertySet_t Model::cloneBindingAsGhost( const XPropertySet_t &xBinding )
throw( RuntimeException )
{
// Create a new binding instance first...
Binding *pBinding = new Binding();
// ...and bump up the "defered 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 XPropertySet_t& xBinding )
throw( RuntimeException )
{
Binding* pBinding = Binding::getBinding( xBinding );
if( pBinding != NULL )
{
if( ! pBinding->isUseful() )
mpBindings->removeItem( pBinding );
}
}
Model::XDocument_t Model::newInstance( const rtl::OUString& sName,
const rtl::OUString& sURL,
sal_Bool bURLOnce )
throw( RuntimeException )
{
// create a default instance with <instanceData> element
XDocument_t xInstance = getDocumentBuilder()->newDocument();
DBG_ASSERT( xInstance.is(), "failed to create DOM instance" );
Reference<XNode>( xInstance, UNO_QUERY_THROW )->appendChild(
Reference<XNode>( xInstance->createElement( OUSTRING("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 = mpInstances->addItem( aSequence );
loadInstance( nInstance );
return xInstance;
}
sal_Int32 lcl_findProp( const PropertyValue* pValues,
sal_Int32 nLength,
const rtl::OUString& 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,
const rtl::OUString& 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, NULL, NULL, NULL );
bFound = ( sName == rName );
}
return bFound ? ( n - 1 ) : -1;
}
void Model::renameInstance( const rtl::OUString& sFrom,
const rtl::OUString& sTo,
const rtl::OUString& sURL,
sal_Bool bURLOnce )
throw( RuntimeException )
{
sal_Int32 nPos = lcl_findInstance( mpInstances, sFrom );
if( nPos != -1 )
{
Sequence<PropertyValue> aSeq = mpInstances->getItem( nPos );
PropertyValue* pSeq = aSeq.getArray();
sal_Int32 nLength = aSeq.getLength();
sal_Int32 nProp = lcl_findProp( pSeq, nLength, OUSTRING("ID") );
if( nProp == -1 )
{
// add name property
aSeq.realloc( nLength + 1 );
pSeq = aSeq.getArray();
pSeq[ nLength ].Name = OUSTRING("ID");
nProp = nLength;
}
// change name
pSeq[ nProp ].Value <<= sTo;
// change url
nProp = lcl_findProp( pSeq, nLength, OUSTRING("URL") );
if(nProp != -1)
pSeq[ nProp ].Value <<= sURL;
// change urlonce
nProp = lcl_findProp( pSeq, nLength, OUSTRING("URLOnce") );
if(nProp != -1)
pSeq[ nProp ].Value <<= bURLOnce;
// set instance
mpInstances->setItem( nPos, aSeq );
}
}
void Model::removeInstance( const rtl::OUString& sName )
throw( RuntimeException )
{
sal_Int32 nPos = lcl_findInstance( mpInstances, sName );
if( nPos != -1 )
mpInstances->removeItem( mpInstances->getItem( nPos ) );
}
Reference<XNameContainer> lcl_getModels(
const Reference<com::sun::star::frame::XModel>& xComponent )
{
Reference<XNameContainer> xRet;
Reference<XFormsSupplier> xSupplier( xComponent, UNO_QUERY );
if( xSupplier.is() )
{
xRet = xSupplier->getXForms();
}
return xRet;
}
Model::XModel_t Model::newModel( const Reference<com::sun::star::frame::XModel>& xCmp,
const OUString& sName )
throw( RuntimeException )
{
Model::XModel_t xModel;
Reference<XNameContainer> xModels = lcl_getModels( xCmp );
if( xModels.is()
&& ! xModels->hasByName( sName ) )
{
Model* pModel = new Model();
xModel.set( pModel );
pModel->setID( sName );
pModel->newInstance( OUString(), OUString(), sal_False );
pModel->initialize();
xModels->insertByName( sName, makeAny( xModel ) );
}
return xModel;
}
void Model::renameModel( const Reference<com::sun::star::frame::XModel>& xCmp,
const OUString& sFrom,
const OUString& sTo )
throw( RuntimeException )
{
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, makeAny( xModel ) );
xModels->removeByName( sFrom );
}
}
void Model::removeModel( const Reference<com::sun::star::frame::XModel>& xCmp,
const OUString& sName )
throw( RuntimeException )
{
Reference<XNameContainer> xModels = lcl_getModels( xCmp );
if( xModels.is()
&& xModels->hasByName( sName ) )
{
xModels->removeByName( sName );
}
}
Model::XNode_t Model::createElement( const XNode_t& xParent,
const OUString& sName )
throw( RuntimeException )
{
Reference<XNode> xNode;
if( xParent.is()
&& isValidXMLName( sName ) )
{
// TODO: implement proper namespace handling
xNode.set( xParent->getOwnerDocument()->createElement( sName ),
UNO_QUERY );
}
return xNode;
}
Model::XNode_t Model::createAttribute( const XNode_t& xParent,
const OUString& sName )
throw( RuntimeException )
{
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::valueOf( nCount );
}
// TODO: implement proper namespace handling
xNode.set( xParent->getOwnerDocument()->createAttribute( sUniqueName ),
UNO_QUERY );
}
return xNode;
}
Model::XNode_t Model::renameNode( const XNode_t& xNode,
const rtl::OUString& sName )
throw( RuntimeException )
{
// early out if we don't have to change the name
if( xNode->getNodeName() == sName )
return xNode;
// refuse to change name if its 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.set( xElem, UNO_QUERY );
// 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;
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( Reference<XNode>( xAttr, UNO_QUERY ) );
}
else
{
OSL_ENSURE( false, "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 < mpBindings->countItems(); n++ )
{
Binding* pBinding = Binding::getBinding(
mpBindings->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;
}
Model::XPropertySet_t Model::getBindingForNode( const XNode_t& xNode,
sal_Bool bCreate )
throw( RuntimeException )
{
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.
Binding* pBestBinding = NULL;
sal_Int32 nBestScore = 0;
for( sal_Int32 n = 0; n < mpBindings->countItems(); n++ )
{
Binding* pBinding = Binding::getBinding(
mpBindings->Collection<XPropertySet_t>::getItem( n ) );
OSL_ENSURE( pBinding != NULL, "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 == NULL ),
"score != binding?" );
if( bCreate && pBestBinding == NULL )
{
pBestBinding = new Binding();
pBestBinding->setBindingExpression(
getDefaultBindingExpressionForNode( xNode ) );
mpBindings->addItem( pBestBinding );
}
return pBestBinding;
}
void Model::removeBindingForNode( const XNode_t& )
throw( RuntimeException )
{
// determine whether suitable binding is still used
}
OUString lcl_serializeForDisplay( const Reference< XAttr >& _rxAttrNode )
{
::rtl::OUString sResult;
OSL_ENSURE( _rxAttrNode.is(), "lcl_serializeForDisplay( attr ): invalid argument!" );
if ( _rxAttrNode.is() )
{
::rtl::OUStringBuffer aBuffer;
aBuffer.append( _rxAttrNode->getName() );
aBuffer.appendAscii( "=" );
::rtl::OUString sValue = _rxAttrNode->getValue();
sal_Unicode nQuote = '"';
if ( sValue.indexOf( nQuote ) >= 0 )
nQuote = '\'';
aBuffer.append( nQuote );
aBuffer.append( sValue );
aBuffer.append( nQuote );
aBuffer.append( (sal_Unicode)' ' );
sResult = aBuffer.makeStringAndClear();
}
return sResult;
}
OUString lcl_serializeForDisplay( const Reference<XNodeList>& xNodes )
{
::rtl::OUString sResult;
// create document fragment
Reference<XDocument> xDocument( getDocumentBuilder()->newDocument() );
Reference<XDocumentFragment> xFragment(
xDocument->createDocumentFragment() );
Reference<XNode> xNode( xFragment, UNO_QUERY );
OSL_ENSURE( xFragment.is(), "xFragment" );
OSL_ENSURE( xNode.is(), "xNode" );
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 += lcl_serializeForDisplay( xAttr );
++nAttributeNodes;
}
}
continue;
default:
break;
}
// append node
xNode->appendChild( xDocument->importNode( xCurrent, sal_True ) );
}
OSL_ENSURE( ( nAttributeNodes == 0 ) || ( nAttributeNodes == nLength ),
"lcl_serializeForDisplay: mixed attribute and non-attribute nodes?" );
if ( nAttributeNodes )
// had only attribute nodes
return sResult;
// serialize fragment
CSerializationAppXML aSerialization;
aSerialization.setSource( xFragment );
aSerialization.serialize();
// copy stream into buffer
Reference<XTextInputStream> xTextInputStream(
createInstance( OUSTRING("com.sun.star.io.TextInputStream") ),
UNO_QUERY );
Reference<XActiveDataSink>( xTextInputStream, UNO_QUERY_THROW )
->setInputStream( aSerialization.getInputStream() );
/* WORK AROUND for problem in serialization: currently, multiple
XML delarations (<?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.
OUStringBuffer aBuffer;
while( ! xTextInputStream->isEOF() )
{
OUString sLine = xTextInputStream->readLine();
if( sLine.getLength() > 0
&& sLine.compareToAscii( "<?xml", 5 ) != 0 )
{
aBuffer.append( sLine );
aBuffer.append( sal_Unicode('\n') );
}
}
sResult = aBuffer.makeStringAndClear();
return sResult;
}
OUString lcl_serializeForDisplay( const Reference<XXPathObject>& xResult )
{
// error handling first
if( ! xResult.is() )
return getResource( RID_STR_XFORMS_CANT_EVALUATE );
// TODO: localize
OUStringBuffer aBuffer;
switch( xResult->getObjectType() )
{
case XPathObjectType_XPATH_BOOLEAN:
aBuffer.append( xResult->getBoolean()
? OUSTRING("true")
: OUSTRING("false") );
break;
case XPathObjectType_XPATH_STRING:
aBuffer.append( sal_Unicode('"') );
aBuffer.append( xResult->getString() );
aBuffer.append( sal_Unicode('"') );
break;
case XPathObjectType_XPATH_NODESET:
aBuffer.append( lcl_serializeForDisplay( xResult->getNodeList() ) );
break;
case XPathObjectType_XPATH_NUMBER:
aBuffer.append( xResult->getDouble() );
break;
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?
break;
}
return aBuffer.makeStringAndClear();
}
OUString Model::getResultForExpression(
const XPropertySet_t& xBinding,
sal_Bool bIsBindingExpression,
const OUString& sExpression )
throw( RuntimeException )
{
Binding* pBinding = Binding::getBinding( xBinding );
if( pBinding == NULL )
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( std::vector<EvaluationContext>::iterator aIter = aContext.begin();
aIter != aContext.end();
aIter ++ )
{
aExpression.evaluate( *aIter );
aBuffer.append( lcl_serializeForDisplay(aExpression.getXPath()) );
aBuffer.append( sal_Unicode('\n') );
}
}
return aBuffer.makeStringAndClear();
}
sal_Bool Model::isValidXMLName( const OUString& sName )
throw( RuntimeException )
{
return isValidQName( sName, NULL );
}
sal_Bool Model::isValidPrefixName( const OUString& sName )
throw( RuntimeException )
{
return ::isValidPrefixName( sName, NULL );
}
void Model::setNodeValue(
const XNode_t& xNode,
const rtl::OUString& sValue )
throw( RuntimeException )
{
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];
#define PROP(NAME) \
if( p##NAME != NULL && \
rValue.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(#NAME)) ) \
rValue.Value >>= (*p##NAME)
PROP(ID);
PROP(Instance);
PROP(URL);
PROP(URLOnce);
#undef PROP
}
}
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.getLength() > 0 ) ? &sID : NULL;
const Reference<XDocument>* pInstance = xInstance.is() ? &xInstance : NULL;
const OUString* pURL = ( sURL.getLength() > 0 ) ? &sURL : NULL;
const bool* pURLOnce = ( bURLOnce && pURL != NULL ) ? &bURLOnce : NULL;
// determine new instance data
#define PROP(NAME) if( _p##NAME != NULL ) p##NAME = _p##NAME
PROP(ID);
PROP(Instance);
PROP(URL);
PROP(URLOnce);
#undef PROP
// count # of values we want to set
sal_Int32 nCount = 0;
#define PROP(NAME) if( p##NAME != NULL ) nCount++
PROP(ID);
PROP(Instance);
PROP(URL);
PROP(URLOnce);
#undef PROP
// realloc sequence and enter values;
aSequence.realloc( nCount );
PropertyValue* pSequence = aSequence.getArray();
sal_Int32 nIndex = 0;
#define PROP(NAME) \
if( p##NAME != NULL ) \
{ \
pSequence[ nIndex ].Name = OUSTRING(#NAME); \
pSequence[ nIndex ].Value <<= *p##NAME; \
nIndex++; \
}
PROP(ID);
PROP(Instance);
PROP(URL);
PROP(URLOnce);
#undef PROP
}