1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/any.hxx>
23 #include <comphelper/anytohash.hxx>
24 #include <svx/sdasitm.hxx>
26 #include <com/sun/star/beans/PropertyValue.hpp>
28 using namespace com::sun::star
;
31 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem()
32 : SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
)
35 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem( const uno::Sequence
< beans::PropertyValue
>& rVal
)
36 : SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
)
41 css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rPropName
)
43 ASSERT_CHANGE_REFCOUNTED_ITEM
;
44 css::uno::Any
* pRet
= nullptr;
45 PropertyHashMap::iterator
aHashIter( m_aPropHashMap
.find( rPropName
) );
46 if ( aHashIter
!= m_aPropHashMap
.end() )
47 pRet
= &m_aPropSeq
.getArray()[ (*aHashIter
).second
].Value
;
51 const css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rPropName
) const
53 const css::uno::Any
* pRet
= nullptr;
54 PropertyHashMap::const_iterator
aHashIter( m_aPropHashMap
.find( rPropName
) );
55 if ( aHashIter
!= m_aPropHashMap
.end() )
56 pRet
= &m_aPropSeq
[ (*aHashIter
).second
].Value
;
60 css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rSequenceName
, const OUString
& rPropName
)
62 ASSERT_CHANGE_REFCOUNTED_ITEM
;
63 css::uno::Any
* pRet
= nullptr;
64 css::uno::Any
* pSeqAny
= GetPropertyValueByName( rSequenceName
);
67 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pSeqAny
) )
69 PropertyPairHashMap::iterator
aHashIter( m_aPropPairHashMap
.find( PropertyPair( rSequenceName
, rPropName
) ) );
70 if ( aHashIter
!= m_aPropPairHashMap
.end() )
72 pRet
= &const_cast<css::uno::Sequence
<css::beans::PropertyValue
> &>(*rSecSequence
).getArray()[ (*aHashIter
).second
].Value
;
79 const css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rSequenceName
, const OUString
& rPropName
) const
81 const css::uno::Any
* pRet
= nullptr;
82 const css::uno::Any
* pSeqAny
= GetPropertyValueByName( rSequenceName
);
85 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pSeqAny
) )
87 PropertyPairHashMap::const_iterator
aHashIter( m_aPropPairHashMap
.find( PropertyPair( rSequenceName
, rPropName
) ) );
88 if ( aHashIter
!= m_aPropPairHashMap
.end() )
90 pRet
= &(*rSecSequence
)[ (*aHashIter
).second
].Value
;
97 void SdrCustomShapeGeometryItem::SetPropertyValue( const css::beans::PropertyValue
& rPropVal
)
99 ASSERT_CHANGE_REFCOUNTED_ITEM
;
100 css::uno::Any
* pAny
= GetPropertyValueByName( rPropVal
.Name
);
102 { // property is already available
103 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pAny
) )
104 { // old property is a sequence->each entry has to be removed from the HashPairMap
105 for ( auto const & i
: *rSecSequence
)
107 PropertyPairHashMap::iterator
aHashIter( m_aPropPairHashMap
.find( PropertyPair( rPropVal
.Name
, i
.Name
) ) );
108 if ( aHashIter
!= m_aPropPairHashMap
.end() )
109 m_aPropPairHashMap
.erase( aHashIter
);
112 *pAny
= rPropVal
.Value
;
113 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pAny
) )
114 { // the new property is a sequence->each entry has to be inserted into the HashPairMap
115 for ( sal_Int32 i
= 0; i
< rSecSequence
->getLength(); i
++ )
117 beans::PropertyValue
const & rPropVal2
= (*rSecSequence
)[ i
];
118 m_aPropPairHashMap
[ PropertyPair( rPropVal
.Name
, rPropVal2
.Name
) ] = i
;
123 { // it's a new property
124 assert(std::none_of(std::cbegin(m_aPropSeq
), std::cend(m_aPropSeq
),
125 [&rPropVal
](beans::PropertyValue
const& rVal
)
126 { return rVal
.Name
== rPropVal
.Name
; } ));
127 sal_uInt32 nIndex
= m_aPropSeq
.getLength();
128 m_aPropSeq
.realloc( nIndex
+ 1 );
129 m_aPropSeq
.getArray()[ nIndex
] = rPropVal
;
131 m_aPropHashMap
[ rPropVal
.Name
] = nIndex
;
136 void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString
& rSequenceName
, const css::beans::PropertyValue
& rPropVal
)
138 ASSERT_CHANGE_REFCOUNTED_ITEM
;
139 css::uno::Any
* pAny
= GetPropertyValueByName( rSequenceName
, rPropVal
.Name
);
140 if ( pAny
) // just replacing
141 *pAny
= rPropVal
.Value
;
144 css::uno::Any
* pSeqAny
= GetPropertyValueByName( rSequenceName
);
145 if( pSeqAny
== nullptr )
147 css::uno::Sequence
< beans::PropertyValue
> aSeq
;
148 beans::PropertyValue aValue
;
149 aValue
.Name
= rSequenceName
;
150 aValue
.Value
<<= aSeq
;
152 assert(std::none_of(std::cbegin(m_aPropSeq
), std::cend(m_aPropSeq
),
153 [&rSequenceName
](beans::PropertyValue
const& rV
)
154 { return rV
.Name
== rSequenceName
; } ));
155 sal_uInt32 nIndex
= m_aPropSeq
.getLength();
156 m_aPropSeq
.realloc( nIndex
+ 1 );
157 auto pPropSeq
= m_aPropSeq
.getArray();
158 pPropSeq
[ nIndex
] = std::move(aValue
);
159 m_aPropHashMap
[ rSequenceName
] = nIndex
;
161 pSeqAny
= &pPropSeq
[ nIndex
].Value
;
164 if (auto pSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pSeqAny
))
166 PropertyPairHashMap::iterator
aHashIter(
167 m_aPropPairHashMap
.find(PropertyPair(rSequenceName
, rPropVal
.Name
)));
168 auto& rSeq
= const_cast<css::uno::Sequence
<css::beans::PropertyValue
>&>(*pSecSequence
);
169 if (aHashIter
!= m_aPropPairHashMap
.end())
171 rSeq
.getArray()[(*aHashIter
).second
].Value
= rPropVal
.Value
;
175 const sal_Int32 nCount
= pSecSequence
->getLength();
176 rSeq
.realloc(nCount
+ 1);
177 rSeq
.getArray()[nCount
] = rPropVal
;
179 m_aPropPairHashMap
[PropertyPair(rSequenceName
, rPropVal
.Name
)] = nCount
;
186 void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString
& rPropName
)
188 ASSERT_CHANGE_REFCOUNTED_ITEM
;
189 if ( !m_aPropSeq
.hasElements() )
192 PropertyHashMap::iterator
aHashIter( m_aPropHashMap
.find( rPropName
) );
193 if ( aHashIter
== m_aPropHashMap
.end() )
196 auto pPropSeq
= m_aPropSeq
.getArray();
197 css::uno::Any
& rSeqAny
= pPropSeq
[(*aHashIter
).second
].Value
;
198 if (auto pSecSequence
199 = o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(rSeqAny
))
201 for (const auto& rPropVal
: *pSecSequence
)
203 auto _aHashIter(m_aPropPairHashMap
.find(PropertyPair(rPropName
, rPropVal
.Name
)));
204 if (_aHashIter
!= m_aPropPairHashMap
.end())
205 m_aPropPairHashMap
.erase(_aHashIter
); // removing property from pair hashmap
208 sal_Int32 nLength
= m_aPropSeq
.getLength();
211 sal_Int32 nIndex
= (*aHashIter
).second
;
212 if ( nIndex
!= ( nLength
- 1 ) ) // resizing sequence
214 PropertyHashMap::iterator
aHashIter2( m_aPropHashMap
.find( m_aPropSeq
[ nLength
- 1 ].Name
) );
215 assert(aHashIter2
!= m_aPropHashMap
.end());
216 (*aHashIter2
).second
= nIndex
;
217 pPropSeq
[ nIndex
] = m_aPropSeq
[ nLength
- 1 ];
219 m_aPropSeq
.realloc( nLength
- 1 );
221 m_aPropHashMap
.erase( aHashIter
); // removing property from hashmap
225 SdrCustomShapeGeometryItem::~SdrCustomShapeGeometryItem()
229 bool SdrCustomShapeGeometryItem::operator==( const SfxPoolItem
& rCmp
) const
231 if( !SfxPoolItem::operator==( rCmp
))
233 const SdrCustomShapeGeometryItem
& other
= static_cast<const SdrCustomShapeGeometryItem
&>(rCmp
);
234 // This is called often by SfxItemPool, and comparing uno sequences is relatively slow.
235 // So keep a hash of the sequence and if either of the sequences has a usable hash,
236 // compare using that.
239 if( m_aHashState
!= other
.m_aHashState
)
241 if( m_aHashState
== HashState::Valid
&& m_aHash
!= other
.m_aHash
)
244 return m_aPropSeq
== other
.m_aPropSeq
;
247 void SdrCustomShapeGeometryItem::UpdateHash() const
249 if( m_aHashState
!= HashState::Unknown
)
251 std::optional
< size_t > hash
= comphelper::anyToHash( css::uno::Any( m_aPropSeq
));
252 if( hash
.has_value())
255 m_aHashState
= HashState::Valid
;
258 m_aHashState
= HashState::Unusable
;
261 void SdrCustomShapeGeometryItem::InvalidateHash()
263 m_aHashState
= HashState::Unknown
;
266 bool SdrCustomShapeGeometryItem::GetPresentation(
267 SfxItemPresentation ePresentation
, MapUnit
/*eCoreMetric*/,
268 MapUnit
/*ePresentationMetric*/, OUString
&rText
, const IntlWrapper
&) const
271 if ( ePresentation
== SfxItemPresentation::Complete
)
276 else if ( ePresentation
== SfxItemPresentation::Nameless
)
281 SdrCustomShapeGeometryItem
* SdrCustomShapeGeometryItem::Clone( SfxItemPool
* /*pPool*/ ) const
283 return new SdrCustomShapeGeometryItem( m_aPropSeq
);
286 bool SdrCustomShapeGeometryItem::QueryValue( uno::Any
& rVal
, sal_uInt8
/*nMemberId*/ ) const
292 bool SdrCustomShapeGeometryItem::PutValue( const uno::Any
& rVal
, sal_uInt8
/*nMemberId*/ )
294 ASSERT_CHANGE_REFCOUNTED_ITEM
;
295 css::uno::Sequence
< css::beans::PropertyValue
> propSeq
;
296 if ( ! ( rVal
>>= propSeq
) )
299 SetPropSeq( propSeq
);
303 void SdrCustomShapeGeometryItem::SetPropSeq( const css::uno::Sequence
< css::beans::PropertyValue
>& rVal
)
305 ASSERT_CHANGE_REFCOUNTED_ITEM
;
306 if( m_aPropSeq
== rVal
)
310 m_aPropHashMap
.clear();
311 m_aPropPairHashMap
.clear();
312 for ( sal_Int32 i
= 0; i
< m_aPropSeq
.getLength(); i
++ )
314 const beans::PropertyValue
& rPropVal
= m_aPropSeq
[ i
];
315 std::pair
<PropertyHashMap::iterator
, bool> const ret(
316 m_aPropHashMap
.insert(std::make_pair(rPropVal
.Name
, i
)));
317 assert(ret
.second
); // serious bug: duplicate xml attribute exported
320 throw uno::RuntimeException(
321 "CustomShapeGeometry has duplicate property " + rPropVal
.Name
);
323 if (auto rPropSeq
= o3tl::tryAccess
<uno::Sequence
<beans::PropertyValue
>>(
326 for ( sal_Int32 j
= 0; j
< rPropSeq
->getLength(); j
++ )
328 beans::PropertyValue
const & rPropVal2
= (*rPropSeq
)[ j
];
329 m_aPropPairHashMap
[ PropertyPair( rPropVal
.Name
, rPropVal2
.Name
) ] = j
;
336 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */