office-gobmx/xmloff/source/style/xmlexppr.cxx
Andrea Gelmini 102cbf4626 Fix "lets" -> "let's"
Change-Id: I01968fc18b093dbbc27213f01c3da38ae151c62c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169748
Reviewed-by: Andrea Gelmini <andrea.gelmini@gelma.net>
Tested-by: Jenkins
2024-06-29 17:22:12 +02:00

1123 lines
42 KiB
C++

/* -*- 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 <optional>
#include <string_view>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/xml/AttributeData.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#include <com/sun/star/beans/XTolerantMultiPropertySet.hpp>
#include <com/sun/star/beans/TolerantPropertySetResultType.hpp>
#include <comphelper/anycompare.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <cppuhelper/weakref.hxx>
#include <osl/diagnose.h>
#include <list>
#include <map>
#include <o3tl/sorted_vector.hxx>
#include <utility>
#include <xmloff/xmlexppr.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmlexp.hxx>
#include <xmloff/xmlprmap.hxx>
#include <xmloff/maptype.hxx>
#include <xmloff/xmltypes.hxx>
#include <xmloff/xmlprhdl.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::uno;
using namespace ::xmloff::token;
#define GET_PROP_TYPE( f ) static_cast<sal_uInt16>((f & XML_TYPE_PROP_MASK) >> XML_TYPE_PROP_SHIFT)
namespace {
struct XMLPropTokens_Impl
{
sal_uInt16 nType;
XMLTokenEnum eToken;
};
const sal_uInt16 MAX_PROP_TYPES =
(XML_TYPE_PROP_END >> XML_TYPE_PROP_SHIFT) -
(XML_TYPE_PROP_START >> XML_TYPE_PROP_SHIFT);
XMLPropTokens_Impl const aPropTokens[MAX_PROP_TYPES] =
{
{ GET_PROP_TYPE(XML_TYPE_PROP_CHART), XML_CHART_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_GRAPHIC), XML_GRAPHIC_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_TABLE), XML_TABLE_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_TABLE_COLUMN), XML_TABLE_COLUMN_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_TABLE_ROW), XML_TABLE_ROW_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_TABLE_CELL), XML_TABLE_CELL_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_LIST_LEVEL), XML_LIST_LEVEL_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_PARAGRAPH), XML_PARAGRAPH_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_TEXT), XML_TEXT_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_DRAWING_PAGE), XML_DRAWING_PAGE_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_PAGE_LAYOUT), XML_PAGE_LAYOUT_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_HEADER_FOOTER), XML_HEADER_FOOTER_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_RUBY), XML_RUBY_PROPERTIES },
{ GET_PROP_TYPE(XML_TYPE_PROP_SECTION), XML_SECTION_PROPERTIES }
};
// public methods
// Take all properties of the XPropertySet which are also found in the
// XMLPropertyMapEntry-array and which are not set to their default-value,
// if a state is available.
// After that I call the method 'ContextFilter'.
struct ComparePropertyState
{
bool operator()(XMLPropertyState const& lhs, XMLPropertyState const& rhs)
{
return lhs.mnIndex < rhs.mnIndex;
}
};
class XMLPropertyStates_Impl
{
o3tl::sorted_vector<XMLPropertyState, ComparePropertyState> aPropStates;
public:
XMLPropertyStates_Impl();
void AddPropertyState(const XMLPropertyState& rPropState);
void FillPropertyStateVector(std::vector<XMLPropertyState>& rVector);
};
XMLPropertyStates_Impl::XMLPropertyStates_Impl()
{
}
void XMLPropertyStates_Impl::AddPropertyState(
const XMLPropertyState& rPropState)
{
aPropStates.insert(rPropState);
}
void XMLPropertyStates_Impl::FillPropertyStateVector(
std::vector<XMLPropertyState>& rVector)
{
rVector.insert( rVector.begin(), aPropStates.begin(), aPropStates.end() );
}
class FilterPropertyInfo_Impl
{
OUString msApiName;
std::vector<sal_uInt32> maIndexes;
public:
FilterPropertyInfo_Impl( OUString aApiName,
const sal_uInt32 nIndex);
const OUString& GetApiName() const { return msApiName; }
std::vector<sal_uInt32>& GetIndexes() { return maIndexes; }
// for sort
bool operator< ( const FilterPropertyInfo_Impl& rArg ) const
{
return (GetApiName() < rArg.GetApiName());
}
};
FilterPropertyInfo_Impl::FilterPropertyInfo_Impl(
OUString aApiName,
const sal_uInt32 nIndex ) :
msApiName(std::move( aApiName ))
{
maIndexes.push_back(nIndex);
}
typedef std::list<FilterPropertyInfo_Impl> FilterPropertyInfoList_Impl;
class FilterPropertiesInfo_Impl
{
FilterPropertyInfoList_Impl aPropInfos;
std::optional<Sequence<OUString>> mxApiNames;
public:
FilterPropertiesInfo_Impl();
void AddProperty(const OUString& rApiName, const sal_uInt32 nIndex);
const uno::Sequence<OUString>& GetApiNames();
void FillPropertyStateArray(
std::vector< XMLPropertyState >& rPropStates,
const Reference< XPropertySet >& xPropSet,
const rtl::Reference< XMLPropertySetMapper >& maPropMapper,
const bool bDefault);
sal_uInt32 GetPropertyCount() const { return aPropInfos.size(); }
};
FilterPropertiesInfo_Impl::FilterPropertiesInfo_Impl()
{
}
void FilterPropertiesInfo_Impl::AddProperty(
const OUString& rApiName, const sal_uInt32 nIndex)
{
aPropInfos.emplace_back(rApiName, nIndex);
OSL_ENSURE( !mxApiNames, "performance warning: API names already retrieved" );
mxApiNames.reset();
}
const uno::Sequence<OUString>& FilterPropertiesInfo_Impl::GetApiNames()
{
if( !mxApiNames )
{
// we have to do three things:
// 1) sort API names,
// 2) merge duplicates,
// 3) construct sequence
// sort names
aPropInfos.sort();
// merge duplicates
if ( aPropInfos.size() > 1 )
{
FilterPropertyInfoList_Impl::iterator aOld = aPropInfos.begin();
FilterPropertyInfoList_Impl::iterator aEnd = aPropInfos.end();
FilterPropertyInfoList_Impl::iterator aCurrent = aOld;
++aCurrent;
while ( aCurrent != aEnd )
{
// equal to next element?
if ( aOld->GetApiName() == aCurrent->GetApiName() )
{
// if equal: merge index lists
std::vector<sal_uInt32> aMerged;
std::merge(aOld->GetIndexes().begin(), aOld->GetIndexes().end(),
aCurrent->GetIndexes().begin(), aCurrent->GetIndexes().end(),
std::back_inserter(aMerged));
aOld->GetIndexes() = std::move(aMerged);
aCurrent->GetIndexes().clear();
// erase element, and continue with next
aCurrent = aPropInfos.erase( aCurrent );
}
else
{
// remember old element and continue with next
aOld = aCurrent;
++aCurrent;
}
}
}
// construct sequence
mxApiNames.emplace( aPropInfos.size() );
OUString *pNames = mxApiNames->getArray();
for (auto const& propInfo : aPropInfos)
{
*pNames = propInfo.GetApiName();
++pNames;
}
}
return *mxApiNames;
}
void FilterPropertiesInfo_Impl::FillPropertyStateArray(
std::vector< XMLPropertyState >& rPropStates,
const Reference< XPropertySet >& rPropSet,
const rtl::Reference< XMLPropertySetMapper >& rPropMapper,
const bool bDefault )
{
XMLPropertyStates_Impl aPropStates;
const uno::Sequence<OUString>& rApiNames = GetApiNames();
Reference < XTolerantMultiPropertySet > xTolPropSet( rPropSet, UNO_QUERY );
if (xTolPropSet.is())
{
if (!bDefault)
{
Sequence < beans::GetDirectPropertyTolerantResult > aResults(xTolPropSet->getDirectPropertyValuesTolerant(rApiNames));
sal_Int32 nResultCount(aResults.getLength());
if (nResultCount > 0)
{
const beans::GetDirectPropertyTolerantResult *pResults = aResults.getConstArray();
FilterPropertyInfoList_Impl::iterator aPropIter(aPropInfos.begin());
XMLPropertyState aNewProperty( -1 );
while (nResultCount > 0 && aPropIter != aPropInfos.end())
{
if (pResults->Name == aPropIter->GetApiName())
{
aNewProperty.mnIndex = -1;
aNewProperty.maValue = pResults->Value;
for (auto const& index : aPropIter->GetIndexes())
{
aNewProperty.mnIndex = index;
aPropStates.AddPropertyState( aNewProperty );
}
++pResults;
--nResultCount;
}
++aPropIter;
}
}
}
else
{
const Sequence < beans::GetPropertyTolerantResult > aResults(xTolPropSet->getPropertyValuesTolerant(rApiNames));
OSL_ENSURE( rApiNames.getLength() == aResults.getLength(), "wrong implemented XTolerantMultiPropertySet" );
FilterPropertyInfoList_Impl::iterator aPropIter(aPropInfos.begin());
XMLPropertyState aNewProperty( -1 );
OSL_ENSURE( aPropInfos.size() == static_cast<sal_uInt32>(aResults.getLength()), "wrong implemented XTolerantMultiPropertySet??" );
for( const auto& rResult : aResults )
{
if ((rResult.Result == beans::TolerantPropertySetResultType::SUCCESS) &&
((rResult.State == PropertyState_DIRECT_VALUE) || (rResult.State == PropertyState_DEFAULT_VALUE)))
{
aNewProperty.mnIndex = -1;
aNewProperty.maValue = rResult.Value;
for (auto const& index : aPropIter->GetIndexes())
{
aNewProperty.mnIndex = index;
aPropStates.AddPropertyState( aNewProperty );
}
}
++aPropIter;
}
}
}
else
{
Sequence < PropertyState > aStates;
const PropertyState *pStates = nullptr;
Reference< XPropertyState > xPropState( rPropSet, UNO_QUERY );
if( xPropState.is() )
{
aStates = xPropState->getPropertyStates( rApiNames );
pStates = aStates.getConstArray();
}
Reference < XMultiPropertySet > xMultiPropSet( rPropSet, UNO_QUERY );
if( xMultiPropSet.is() && !bDefault )
{
Sequence < Any > aValues;
if( pStates )
{
// step 1: get value count
sal_uInt32 nValueCount = 0;
for (size_t i = 0; i < aPropInfos.size(); ++i, ++pStates)
{
if( *pStates == PropertyState_DIRECT_VALUE )
nValueCount++;
}
if( nValueCount )
{
// step 2: collect property names
Sequence < OUString > aAPINames( nValueCount );
OUString *pAPINames = aAPINames.getArray();
::std::vector< FilterPropertyInfoList_Impl::iterator > aPropIters;
aPropIters.reserve( nValueCount );
FilterPropertyInfoList_Impl::iterator aItr = aPropInfos.begin();
OSL_ENSURE(aItr != aPropInfos.end(),"Invalid iterator!");
pStates = aStates.getConstArray();
sal_uInt32 i = 0;
while( i < nValueCount )
{
if( *pStates == PropertyState_DIRECT_VALUE )
{
*pAPINames++ = aItr->GetApiName();
aPropIters.push_back( aItr );
++i;
}
++aItr;
++pStates;
}
aValues = xMultiPropSet->getPropertyValues( aAPINames );
const Any *pValues = aValues.getConstArray();
::std::vector< FilterPropertyInfoList_Impl::iterator >::const_iterator
pPropIter = aPropIters.begin();
XMLPropertyState aNewProperty( -1 );
for( i = 0; i < nValueCount; ++i )
{
aNewProperty.mnIndex = -1;
aNewProperty.maValue = *pValues;
for (auto const& index : (*pPropIter)->GetIndexes())
{
aNewProperty.mnIndex = index;
aPropStates.AddPropertyState( aNewProperty );
}
++pPropIter;
++pValues;
}
}
}
else
{
aValues = xMultiPropSet->getPropertyValues( rApiNames );
const Any *pValues = aValues.getConstArray();
FilterPropertyInfoList_Impl::iterator aItr = aPropInfos.begin();
for (size_t i = 0; i < aPropInfos.size(); ++i)
{
// The value is stored in the PropertySet itself, add to list.
XMLPropertyState aNewProperty( -1 );
aNewProperty.maValue = *pValues;
++pValues;
for (auto const& index : aItr->GetIndexes())
{
aNewProperty.mnIndex = index;
aPropStates.AddPropertyState( aNewProperty );
}
++aItr;
}
}
}
else
{
FilterPropertyInfoList_Impl::iterator aItr = aPropInfos.begin();
for (size_t i = 0; i < aPropInfos.size(); ++i)
{
bool bDirectValue =
!pStates || *pStates == PropertyState_DIRECT_VALUE;
if( bDirectValue || bDefault )
{
// The value is stored in the PropertySet itself, add to list.
bool bGotValue = false;
XMLPropertyState aNewProperty( -1 );
for (auto const& index : aItr->GetIndexes())
{
if( bDirectValue ||
(rPropMapper->GetEntryFlags(index) &
MID_FLAG_DEFAULT_ITEM_EXPORT) != 0 )
{
try
{
if( !bGotValue )
{
aNewProperty.maValue =
rPropSet->getPropertyValue( aItr->GetApiName() );
bGotValue = true;
}
aNewProperty.mnIndex = index;
aPropStates.AddPropertyState( aNewProperty );
}
catch( UnknownPropertyException& )
{
// might be a problem of getImplementationId
TOOLS_WARN_EXCEPTION("xmloff.style", "unknown property in getPropertyValue" );
}
}
}
}
++aItr;
if( pStates )
++pStates;
}
}
}
aPropStates.FillPropertyStateVector(rPropStates);
}
}
struct SvXMLExportPropertyMapper::Impl
{
typedef std::map<css::uno::Reference<css::beans::XPropertySetInfo>, std::unique_ptr<FilterPropertiesInfo_Impl>> CacheType;
CacheType maCache;
rtl::Reference<SvXMLExportPropertyMapper> mxNextMapper;
rtl::Reference<XMLPropertySetMapper> mxPropMapper;
OUString maStyleName;
};
// ctor/dtor , class SvXMLExportPropertyMapper
SvXMLExportPropertyMapper::SvXMLExportPropertyMapper(
const rtl::Reference< XMLPropertySetMapper >& rMapper ) :
mpImpl(new Impl)
{
mpImpl->mxPropMapper = rMapper;
}
SvXMLExportPropertyMapper::~SvXMLExportPropertyMapper()
{
}
void SvXMLExportPropertyMapper::ChainExportMapper(
const rtl::Reference< SvXMLExportPropertyMapper>& rMapper )
{
// add map entries from rMapper to current map
mpImpl->mxPropMapper->AddMapperEntry( rMapper->getPropertySetMapper() );
// rMapper uses the same map as 'this'
rMapper->mpImpl->mxPropMapper = mpImpl->mxPropMapper;
// set rMapper as last mapper in current chain
rtl::Reference< SvXMLExportPropertyMapper > xNext = mpImpl->mxNextMapper;
if( xNext.is())
{
while (xNext->mpImpl->mxNextMapper.is())
xNext = xNext->mpImpl->mxNextMapper;
xNext->mpImpl->mxNextMapper = rMapper;
}
else
mpImpl->mxNextMapper = rMapper;
// if rMapper was already chained, correct
// map pointer of successors
xNext = rMapper;
while (xNext->mpImpl->mxNextMapper.is())
{
xNext = xNext->mpImpl->mxNextMapper;
xNext->mpImpl->mxPropMapper = mpImpl->mxPropMapper;
}
}
std::vector<XMLPropertyState> SvXMLExportPropertyMapper::Filter(
SvXMLExport const& rExport,
const uno::Reference<beans::XPropertySet>& rPropSet, bool bEnableFoFontFamily ) const
{
return Filter_(rExport, rPropSet, false, bEnableFoFontFamily);
}
std::vector<XMLPropertyState> SvXMLExportPropertyMapper::FilterDefaults(
SvXMLExport const& rExport,
const uno::Reference<beans::XPropertySet>& rPropSet ) const
{
return Filter_(rExport, rPropSet, true, false/*bEnableFoFontFamily*/);
}
std::vector<XMLPropertyState> SvXMLExportPropertyMapper::Filter_(
SvXMLExport const& rExport,
const Reference<XPropertySet>& xPropSet, bool bDefault, bool bEnableFoFontFamily ) const
{
std::vector< XMLPropertyState > aPropStateArray;
// Retrieve XPropertySetInfo and XPropertyState
Reference< XPropertySetInfo > xInfo( xPropSet->getPropertySetInfo() );
if( !xInfo.is() )
return aPropStateArray;
sal_Int32 nProps = mpImpl->mxPropMapper->GetEntryCount();
FilterPropertiesInfo_Impl *pFilterInfo = nullptr;
Impl::CacheType::iterator aIter = mpImpl->maCache.find(xInfo);
if (aIter != mpImpl->maCache.end())
pFilterInfo = (*aIter).second.get();
bool bDelInfo = false;
if( !pFilterInfo )
{
assert(GetODFDefaultVersion() != SvtSaveOptions::ODFVER_UNKNOWN);
const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(rExport.getSaneDefaultVersion());
pFilterInfo = new FilterPropertiesInfo_Impl;
for( sal_Int32 i=0; i < nProps; i++ )
{
// Are we allowed to ask for the property? (MID_FLAG_NO_PROP..)
// Does the PropertySet contain name of mpEntries-array ?
const OUString& rAPIName = mpImpl->mxPropMapper->GetEntryAPIName( i );
const sal_Int32 nFlags = mpImpl->mxPropMapper->GetEntryFlags( i );
if( (0 == (nFlags & MID_FLAG_NO_PROPERTY_EXPORT)) &&
( (0 != (nFlags & MID_FLAG_MUST_EXIST)) ||
xInfo->hasPropertyByName( rAPIName ) ) )
{
const SvtSaveOptions::ODFSaneDefaultVersion nEarliestODFVersionForExport(
mpImpl->mxPropMapper->GetEarliestODFVersionForExport(i));
// note: only standard ODF versions are allowed here,
// only exception is the unknown future
assert((nEarliestODFVersionForExport & SvtSaveOptions::ODFSVER_EXTENDED) == 0
|| nEarliestODFVersionForExport == SvtSaveOptions::ODFSVER_FUTURE_EXTENDED);
static_assert(SvtSaveOptions::ODFSVER_LATEST_EXTENDED < SvtSaveOptions::ODFSVER_FUTURE_EXTENDED);
/// standard ODF namespaces for elements and attributes
static sal_uInt16 s_OdfNs[] = {
XML_NAMESPACE_OFFICE,
XML_NAMESPACE_STYLE,
XML_NAMESPACE_TEXT,
XML_NAMESPACE_TABLE,
XML_NAMESPACE_DRAW,
XML_NAMESPACE_FO,
XML_NAMESPACE_XLINK,
XML_NAMESPACE_DC,
XML_NAMESPACE_META,
XML_NAMESPACE_NUMBER,
XML_NAMESPACE_PRESENTATION,
XML_NAMESPACE_SVG,
XML_NAMESPACE_CHART,
XML_NAMESPACE_DR3D,
XML_NAMESPACE_MATH,
XML_NAMESPACE_FORM,
XML_NAMESPACE_SCRIPT,
XML_NAMESPACE_CONFIG,
XML_NAMESPACE_DB,
XML_NAMESPACE_XFORMS,
XML_NAMESPACE_SMIL,
XML_NAMESPACE_ANIMATION,
XML_NAMESPACE_XML,
XML_NAMESPACE_XHTML,
XML_NAMESPACE_GRDDL,
};
static bool s_Assert(false);
if (!s_Assert)
{
assert(std::is_sorted(std::begin(s_OdfNs), std::end(s_OdfNs)));
s_Assert = true;
}
//static_assert(std::is_sorted(std::begin(s_OdfNs), std::end(s_OdfNs)));
auto const ns(mpImpl->mxPropMapper->GetEntryNameSpace(i));
auto const iter(std::lower_bound(std::begin(s_OdfNs), std::end(s_OdfNs),
ns));
bool const isExtension(iter == std::end(s_OdfNs) || *iter != ns
// FIXME: very special hack to suppress style:hyperlink
|| (ns == XML_NAMESPACE_STYLE
&& mpImpl->mxPropMapper->GetEntryXMLName(i) == GetXMLToken(XML_HYPERLINK)));
if (isExtension
? ((nCurrentVersion & SvtSaveOptions::ODFSVER_EXTENDED)
// if it's in standard ODF, don't export extension
&& (nCurrentVersion < nEarliestODFVersionForExport))
: (nEarliestODFVersionForExport <= nCurrentVersion))
{
pFilterInfo->AddProperty(rAPIName, i);
}
}
}
// Check whether the property set info is destroyed if it is assigned to
// a weak reference only; If it is destroyed, then every instance of
// getPropertySetInfo returns a new object; such property set infos must
// not be cached:
WeakReference < XPropertySetInfo > xWeakInfo( xInfo );
xInfo.clear();
xInfo = xWeakInfo;
if( xInfo.is() )
{
mpImpl->maCache.emplace(xInfo, std::unique_ptr<FilterPropertiesInfo_Impl>(pFilterInfo));
}
else
bDelInfo = true;
}
if( pFilterInfo->GetPropertyCount() )
{
try
{
pFilterInfo->FillPropertyStateArray(
aPropStateArray, xPropSet, mpImpl->mxPropMapper, bDefault);
}
catch( UnknownPropertyException& )
{
// might be a problem of getImplementationId
TOOLS_WARN_EXCEPTION("xmloff.style", "unknown property in getPropertyStates" );
}
}
// Call context-filter
if( !aPropStateArray.empty() )
ContextFilter(bEnableFoFontFamily, aPropStateArray, xPropSet);
// Have to do if we change from a vector to a list or something like that
if( bDelInfo )
delete pFilterInfo;
return aPropStateArray;
}
void SvXMLExportPropertyMapper::ContextFilter(
bool bEnableFoFontFamily,
std::vector< XMLPropertyState >& rProperties,
const Reference< XPropertySet >& rPropSet ) const
{
// Derived class could implement this.
if (mpImpl->mxNextMapper.is())
mpImpl->mxNextMapper->ContextFilter(bEnableFoFontFamily, rProperties, rPropSet);
}
// Compares two Sequences of XMLPropertyState:
// 1.Number of elements equal ?
// 2.Index of each element equal ? (So I know whether the propertynames are the same)
// 3.Value of each element equal ?
bool SvXMLExportPropertyMapper::Equals(
const std::vector< XMLPropertyState >& aProperties1,
const std::vector< XMLPropertyState >& aProperties2 ) const
{
if (aProperties1.size() < aProperties2.size())
return true;
if (aProperties1.size() > aProperties2.size())
return false;
sal_uInt32 nCount = aProperties1.size();
for (sal_uInt32 nIndex = 0; nIndex < nCount; ++nIndex)
{
const XMLPropertyState& rProp1 = aProperties1[ nIndex ];
const XMLPropertyState& rProp2 = aProperties2[ nIndex ];
// Compare index. If equal, compare value
if( rProp1.mnIndex < rProp2.mnIndex )
return true;
if( rProp1.mnIndex > rProp2.mnIndex )
return false;
if( rProp1.mnIndex != -1 )
{
// Now compare values
if ( (mpImpl->mxPropMapper->GetEntryType( rProp1.mnIndex ) &
XML_TYPE_BUILDIN_CMP ) != 0 )
{
// simple type ( binary compare )
if ( rProp1.maValue != rProp2.maValue)
return false;
}
else
{
// complex type ( ask for compare-function )
if (!mpImpl->mxPropMapper->GetPropertyHandler(
rProp1.mnIndex )->equals( rProp1.maValue,
rProp2.maValue ))
return false;
}
}
}
return true;
}
// Compares two Sequences of XMLPropertyState:
// 1.Number of elements equal ?
// 2.Index of each element equal ? (So I know whether the propertynames are the same)
// 3.Value of each element equal ?
bool SvXMLExportPropertyMapper::LessPartial(
const std::vector< XMLPropertyState >& aProperties1,
const std::vector< XMLPropertyState >& aProperties2 ) const
{
if (aProperties1.size() < aProperties2.size())
return true;
if (aProperties1.size() > aProperties2.size())
return false;
sal_uInt32 nCount = aProperties1.size();
for (sal_uInt32 nIndex = 0; nIndex < nCount; ++nIndex)
{
const XMLPropertyState& rProp1 = aProperties1[ nIndex ];
const XMLPropertyState& rProp2 = aProperties2[ nIndex ];
// Compare index. If equal, compare value
if( rProp1.mnIndex < rProp2.mnIndex )
return true;
if( rProp1.mnIndex > rProp2.mnIndex )
return false;
if( rProp1.mnIndex != -1 )
{
// Now compare values
if ( (mpImpl->mxPropMapper->GetEntryType( rProp1.mnIndex ) &
XML_TYPE_BUILDIN_CMP ) != 0 )
{
// simple type ( binary compare )
if ( comphelper::anyLess(rProp1.maValue, rProp2.maValue) )
return true;
if ( comphelper::anyLess(rProp2.maValue, rProp1.maValue ) )
return false;
}
}
}
return false;
}
/** fills the given attribute list with the items in the given set
void SvXMLExportPropertyMapper::exportXML( SvXMLAttributeList& rAttrList,
const ::std::vector< XMLPropertyState >& rProperties,
const SvXMLUnitConverter& rUnitConverter,
const SvXMLNamespaceMap& rNamespaceMap,
sal_uInt16 nFlags ) const
{
_exportXML( rAttrList, rProperties, rUnitConverter, rNamespaceMap,
nFlags, 0, -1, -1 );
}
void SvXMLExportPropertyMapper::exportXML( SvXMLAttributeList& rAttrList,
const ::std::vector< XMLPropertyState >& rProperties,
const SvXMLUnitConverter& rUnitConverter,
const SvXMLNamespaceMap& rNamespaceMap,
sal_Int32 nPropMapStartIdx, sal_Int32 nPropMapEndIdx,
sal_uInt16 nFlags ) const
{
_exportXML( rAttrList, rProperties, rUnitConverter, rNamespaceMap,
nFlags, 0, nPropMapStartIdx, nPropMapEndIdx );
}
*/
void SvXMLExportPropertyMapper::exportXML(
SvXMLExport& rExport,
const ::std::vector< XMLPropertyState >& rProperties,
SvXmlExportFlags nFlags,
bool bUseExtensionNamespaceForGraphicProperties) const
{
exportXML(rExport, rProperties, -1, -1, nFlags, bUseExtensionNamespaceForGraphicProperties);
}
void SvXMLExportPropertyMapper::exportXML(
SvXMLExport& rExport,
const ::std::vector< XMLPropertyState >& rProperties,
sal_Int32 nPropMapStartIdx, sal_Int32 nPropMapEndIdx,
SvXmlExportFlags nFlags, bool bUseExtensionNamespaceForGraphicProperties) const
{
sal_uInt16 nPropTypeFlags = 0;
for( sal_uInt16 i=0; i<MAX_PROP_TYPES; ++i )
{
sal_uInt16 nPropType = aPropTokens[i].nType;
if( 0==i || (nPropTypeFlags & (1 << nPropType)) != 0 )
{
sal_uInt16 nNamespace = XML_NAMESPACE_STYLE;
if (bUseExtensionNamespaceForGraphicProperties &&
aPropTokens[i].eToken == xmloff::token::XML_GRAPHIC_PROPERTIES)
{
nNamespace = XML_NAMESPACE_LO_EXT;
if ((rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
{
continue; // don't write for ODF <= 1.2
}
}
std::vector<sal_uInt16> aIndexArray;
_exportXML( nPropType, nPropTypeFlags,
rExport.GetAttrList(), rProperties,
rExport.GetMM100UnitConverter(),
rExport.GetNamespaceMap(),
&aIndexArray,
nPropMapStartIdx, nPropMapEndIdx );
if( rExport.GetAttrList().getLength() > 0 ||
!aIndexArray.empty() )
{
SvXMLElementExport aElem( rExport, nNamespace,
aPropTokens[i].eToken,
bool(nFlags & SvXmlExportFlags::IGN_WS),
false );
exportElementItems( rExport, rProperties, nFlags, aIndexArray );
}
}
}
}
/** this method is called for every item that has the
MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
void SvXMLExportPropertyMapper::handleSpecialItem(
comphelper::AttributeList& rAttrList,
const XMLPropertyState& rProperty,
const SvXMLUnitConverter& rUnitConverter,
const SvXMLNamespaceMap& rNamespaceMap,
const ::std::vector< XMLPropertyState > *pProperties,
sal_uInt32 nIdx ) const
{
OSL_ENSURE(mpImpl->mxNextMapper.is(), "special item not handled in xml export");
if (mpImpl->mxNextMapper.is())
mpImpl->mxNextMapper->handleSpecialItem(
rAttrList, rProperty, rUnitConverter, rNamespaceMap, pProperties, nIdx);
}
/** this method is called for every item that has the
MID_FLAG_ELEMENT_EXPORT flag set */
void SvXMLExportPropertyMapper::handleElementItem(
SvXMLExport& rExport,
const XMLPropertyState& rProperty,
SvXmlExportFlags nFlags,
const ::std::vector< XMLPropertyState > *pProperties,
sal_uInt32 nIdx ) const
{
OSL_ENSURE(mpImpl->mxNextMapper.is(), "element item not handled in xml export");
if (mpImpl->mxNextMapper.is())
mpImpl->mxNextMapper->handleElementItem(rExport, rProperty, nFlags, pProperties, nIdx);
}
// protected methods
/** fills the given attribute list with the items in the given set */
void SvXMLExportPropertyMapper::_exportXML(
sal_uInt16 nPropType, sal_uInt16& rPropTypeFlags,
comphelper::AttributeList& rAttrList,
const ::std::vector< XMLPropertyState >& rProperties,
const SvXMLUnitConverter& rUnitConverter,
const SvXMLNamespaceMap& rNamespaceMap,
std::vector<sal_uInt16>* pIndexArray,
sal_Int32 nPropMapStartIdx, sal_Int32 nPropMapEndIdx ) const
{
const sal_uInt32 nCount = rProperties.size();
sal_uInt32 nIndex = 0;
if( -1 == nPropMapStartIdx )
nPropMapStartIdx = 0;
if( -1 == nPropMapEndIdx )
nPropMapEndIdx = mpImpl->mxPropMapper->GetEntryCount();
while( nIndex < nCount )
{
sal_Int32 nPropMapIdx = rProperties[nIndex].mnIndex;
if( nPropMapIdx >= nPropMapStartIdx &&
nPropMapIdx < nPropMapEndIdx )// valid entry?
{
sal_uInt32 nEFlags = mpImpl->mxPropMapper->GetEntryFlags(nPropMapIdx);
sal_uInt16 nEPType = GET_PROP_TYPE(nEFlags);
OSL_ENSURE(nEPType >= (XML_TYPE_PROP_START >> XML_TYPE_PROP_SHIFT),
"no prop type specified");
rPropTypeFlags |= (1 << nEPType);
if( nEPType == nPropType )
{
// we have a valid map entry here, so let's use it...
if( ( nEFlags & MID_FLAG_ELEMENT_ITEM_EXPORT ) != 0 )
{
// element items do not add any properties,
// we export it later
if( pIndexArray )
{
pIndexArray->push_back( static_cast<sal_uInt16>(nIndex) );
}
}
else
{
_exportXML( rAttrList, rProperties[nIndex], rUnitConverter,
rNamespaceMap, &rProperties, nIndex );
}
}
}
nIndex++;
}
}
namespace
{
// -1 = Attribute needs extended namespace, but current ODF version is strict.
// 1 = Attribute needs extended namespace and current ODF version allows it.
// 0 = Attribute does not need extended namespace
sal_Int8 CheckExtendedNamespace(std::u16string_view sXMLAttributeName, std::u16string_view sValue,
const SvtSaveOptions::ODFSaneDefaultVersion nODFVersion)
{
if (IsXMLToken(sXMLAttributeName, XML_WRITING_MODE)
&& (IsXMLToken(sValue, XML_BT_LR) || IsXMLToken(sValue, XML_TB_RL90)))
return nODFVersion & SvtSaveOptions::ODFSVER_EXTENDED ? 1 : -1;
else if (IsXMLToken(sXMLAttributeName, XML_VERTICAL_REL)
&& (IsXMLToken(sValue, XML_PAGE_CONTENT_BOTTOM)
|| IsXMLToken(sValue, XML_PAGE_CONTENT_TOP)))
return nODFVersion & SvtSaveOptions::ODFSVER_EXTENDED ? 1 : -1;
return 0;
}
}
void SvXMLExportPropertyMapper::_exportXML(
comphelper::AttributeList& rAttrList,
const XMLPropertyState& rProperty,
const SvXMLUnitConverter& rUnitConverter,
const SvXMLNamespaceMap& rNamespaceMap,
const ::std::vector< XMLPropertyState > *pProperties,
sal_uInt32 nIdx ) const
{
if ((mpImpl->mxPropMapper->GetEntryFlags(rProperty.mnIndex) & MID_FLAG_SPECIAL_ITEM_EXPORT) != 0)
{
uno::Reference< container::XNameContainer > xAttrContainer;
if( (rProperty.maValue >>= xAttrContainer) && xAttrContainer.is() )
{
std::unique_ptr<SvXMLNamespaceMap> pNewNamespaceMap;
const SvXMLNamespaceMap *pNamespaceMap = &rNamespaceMap;
const uno::Sequence< OUString > aAttribNames( xAttrContainer->getElementNames() );
xml::AttributeData aData;
for( const auto& rAttribName : aAttribNames )
{
xAttrContainer->getByName( rAttribName ) >>= aData;
OUString sAttribName( rAttribName );
// extract namespace prefix from attribute name if it exists
OUString sPrefix;
const sal_Int32 nColonPos =
rAttribName.indexOf( ':' );
if( nColonPos != -1 )
sPrefix = rAttribName.copy( 0, nColonPos );
if( !sPrefix.isEmpty() )
{
OUString sNamespace( aData.Namespace );
// if the prefix isn't defined yet or has another meaning,
// we have to redefine it now.
sal_uInt16 nKey = pNamespaceMap->GetKeyByPrefix( sPrefix );
if( USHRT_MAX == nKey || pNamespaceMap->GetNameByKey( nKey ) != sNamespace )
{
bool bAddNamespace = false;
if( USHRT_MAX == nKey )
{
// The prefix is unused, so it is sufficient
// to add it to the namespace map.
bAddNamespace = true;
}
else
{
// check if there is a prefix registered for the
// namespace URI
nKey = pNamespaceMap->GetKeyByName( sNamespace );
if( XML_NAMESPACE_UNKNOWN == nKey )
{
// There is no prefix for the namespace, so
// we have to generate one and have to add it.
sal_Int32 n=0;
OUString sOrigPrefix( sPrefix );
do
{
sPrefix = sOrigPrefix + OUString::number( ++n );
nKey = pNamespaceMap->GetKeyByPrefix( sPrefix );
}
while( nKey != USHRT_MAX );
bAddNamespace = true;
}
else
{
// If there is a prefix for the namespace,
// we reuse that.
sPrefix = pNamespaceMap->GetPrefixByKey( nKey );
}
// In any case, the attribute name has to be adapted.
sAttribName = sPrefix + ":" + rAttribName.subView(nColonPos+1);
}
if( bAddNamespace )
{
if( !pNewNamespaceMap )
{
pNewNamespaceMap.reset(new SvXMLNamespaceMap( rNamespaceMap ));
pNamespaceMap = pNewNamespaceMap.get();
}
pNewNamespaceMap->Add( sPrefix, sNamespace );
OUString sAttr = GetXMLToken(XML_XMLNS) + ":" + sPrefix;
rAttrList.AddAttribute( sAttr, sNamespace );
}
}
}
OUString sOldValue( rAttrList.getValueByName( sAttribName ) );
OSL_ENSURE( sOldValue.isEmpty(), "alien attribute exists already" );
OSL_ENSURE(aData.Type == GetXMLToken(XML_CDATA), "different type to our default type which should be written out");
if( sOldValue.isEmpty() )
rAttrList.AddAttribute( sAttribName, aData.Value );
}
}
else
{
handleSpecialItem( rAttrList, rProperty, rUnitConverter,
rNamespaceMap, pProperties, nIdx );
}
}
else if ((mpImpl->mxPropMapper->GetEntryFlags(rProperty.mnIndex) & MID_FLAG_ELEMENT_ITEM_EXPORT ) == 0)
{
OUString aValue;
OUString sName = rNamespaceMap.GetQNameByKey(
mpImpl->mxPropMapper->GetEntryNameSpace(rProperty.mnIndex),
mpImpl->mxPropMapper->GetEntryXMLName(rProperty.mnIndex));
bool bRemove = false;
if ((mpImpl->mxPropMapper->GetEntryFlags( rProperty.mnIndex ) & MID_FLAG_MERGE_ATTRIBUTE) != 0)
{
aValue = rAttrList.getValueByName( sName );
bRemove = true;
}
if (mpImpl->mxPropMapper->exportXML(aValue, rProperty, rUnitConverter))
{
if( bRemove )
rAttrList.RemoveAttribute( sName );
// We don't seem to have a generic mechanism to write an attribute in the extension
// namespace in case of certain attribute values only, so do this manually.
sal_Int8 nExtendedStatus
= CheckExtendedNamespace(mpImpl->mxPropMapper->GetEntryXMLName(rProperty.mnIndex),
aValue, rUnitConverter.getSaneDefaultVersion());
if (nExtendedStatus == -1)
return;
if (nExtendedStatus == 1)
sName = rNamespaceMap.GetQNameByKey(
XML_NAMESPACE_LO_EXT, mpImpl->mxPropMapper->GetEntryXMLName(rProperty.mnIndex));
rAttrList.AddAttribute( sName, aValue );
}
}
}
void SvXMLExportPropertyMapper::exportElementItems(
SvXMLExport& rExport,
const ::std::vector< XMLPropertyState >& rProperties,
SvXmlExportFlags nFlags,
const std::vector<sal_uInt16>& rIndexArray ) const
{
bool bItemsExported = false;
for (const sal_uInt16 nElement : rIndexArray)
{
OSL_ENSURE( 0 != (mpImpl->mxPropMapper->GetEntryFlags(
rProperties[nElement].mnIndex ) & MID_FLAG_ELEMENT_ITEM_EXPORT),
"wrong mid flag!" );
rExport.IgnorableWhitespace();
handleElementItem( rExport, rProperties[nElement],
nFlags, &rProperties, nElement );
bItemsExported = true;
}
if( bItemsExported )
rExport.IgnorableWhitespace();
}
const rtl::Reference<XMLPropertySetMapper>& SvXMLExportPropertyMapper::getPropertySetMapper() const
{
return mpImpl->mxPropMapper;
}
void SvXMLExportPropertyMapper::SetStyleName( const OUString& rStyleName )
{
mpImpl->maStyleName = rStyleName;
}
const OUString& SvXMLExportPropertyMapper::GetStyleName() const
{
return mpImpl->maStyleName;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */