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/anycompare.hxx>
24 #include <comphelper/anytohash.hxx>
25 #include <svx/sdasitm.hxx>
27 #include <com/sun/star/beans/PropertyValue.hpp>
29 using namespace com::sun::star
;
32 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem()
33 : SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
)
36 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem( const uno::Sequence
< beans::PropertyValue
>& rVal
)
37 : SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
)
42 css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rPropName
)
44 css::uno::Any
* pRet
= nullptr;
45 PropertyHashMap::iterator
aHashIter( aPropHashMap
.find( rPropName
) );
46 if ( aHashIter
!= aPropHashMap
.end() )
47 pRet
= &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( aPropHashMap
.find( rPropName
) );
55 if ( aHashIter
!= aPropHashMap
.end() )
56 pRet
= &aPropSeq
[ (*aHashIter
).second
].Value
;
60 css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rSequenceName
, const OUString
& rPropName
)
62 css::uno::Any
* pRet
= nullptr;
63 css::uno::Any
* pSeqAny
= GetPropertyValueByName( rSequenceName
);
66 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pSeqAny
) )
68 PropertyPairHashMap::iterator
aHashIter( aPropPairHashMap
.find( PropertyPair( rSequenceName
, rPropName
) ) );
69 if ( aHashIter
!= aPropPairHashMap
.end() )
71 pRet
= &const_cast<css::uno::Sequence
<css::beans::PropertyValue
> &>(*rSecSequence
).getArray()[ (*aHashIter
).second
].Value
;
78 const css::uno::Any
* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString
& rSequenceName
, const OUString
& rPropName
) const
80 const css::uno::Any
* pRet
= nullptr;
81 const css::uno::Any
* pSeqAny
= GetPropertyValueByName( rSequenceName
);
84 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pSeqAny
) )
86 PropertyPairHashMap::const_iterator
aHashIter( aPropPairHashMap
.find( PropertyPair( rSequenceName
, rPropName
) ) );
87 if ( aHashIter
!= aPropPairHashMap
.end() )
89 pRet
= &(*rSecSequence
)[ (*aHashIter
).second
].Value
;
96 void SdrCustomShapeGeometryItem::SetPropertyValue( const css::beans::PropertyValue
& rPropVal
)
98 css::uno::Any
* pAny
= GetPropertyValueByName( rPropVal
.Name
);
100 { // property is already available
101 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pAny
) )
102 { // old property is a sequence->each entry has to be removed from the HashPairMap
103 for ( auto const & i
: *rSecSequence
)
105 PropertyPairHashMap::iterator
aHashIter( aPropPairHashMap
.find( PropertyPair( rPropVal
.Name
, i
.Name
) ) );
106 if ( aHashIter
!= aPropPairHashMap
.end() )
107 aPropPairHashMap
.erase( aHashIter
);
110 *pAny
= rPropVal
.Value
;
111 if ( auto rSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pAny
) )
112 { // the new property is a sequence->each entry has to be inserted into the HashPairMap
113 for ( sal_Int32 i
= 0; i
< rSecSequence
->getLength(); i
++ )
115 beans::PropertyValue
const & rPropVal2
= (*rSecSequence
)[ i
];
116 aPropPairHashMap
[ PropertyPair( rPropVal
.Name
, rPropVal2
.Name
) ] = i
;
121 { // it's a new property
122 assert(std::none_of(std::cbegin(aPropSeq
), std::cend(aPropSeq
),
123 [&rPropVal
](beans::PropertyValue
const& rVal
)
124 { return rVal
.Name
== rPropVal
.Name
; } ));
125 sal_uInt32 nIndex
= aPropSeq
.getLength();
126 aPropSeq
.realloc( nIndex
+ 1 );
127 aPropSeq
.getArray()[ nIndex
] = rPropVal
;
129 aPropHashMap
[ rPropVal
.Name
] = nIndex
;
134 void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString
& rSequenceName
, const css::beans::PropertyValue
& rPropVal
)
136 css::uno::Any
* pAny
= GetPropertyValueByName( rSequenceName
, rPropVal
.Name
);
137 if ( pAny
) // just replacing
138 *pAny
= rPropVal
.Value
;
141 css::uno::Any
* pSeqAny
= GetPropertyValueByName( rSequenceName
);
142 if( pSeqAny
== nullptr )
144 css::uno::Sequence
< beans::PropertyValue
> aSeq
;
145 beans::PropertyValue aValue
;
146 aValue
.Name
= rSequenceName
;
147 aValue
.Value
<<= aSeq
;
149 assert(std::none_of(std::cbegin(aPropSeq
), std::cend(aPropSeq
),
150 [&rSequenceName
](beans::PropertyValue
const& rV
)
151 { return rV
.Name
== rSequenceName
; } ));
152 sal_uInt32 nIndex
= aPropSeq
.getLength();
153 aPropSeq
.realloc( nIndex
+ 1 );
154 auto pPropSeq
= aPropSeq
.getArray();
155 pPropSeq
[ nIndex
] = aValue
;
156 aPropHashMap
[ rSequenceName
] = nIndex
;
158 pSeqAny
= &pPropSeq
[ nIndex
].Value
;
161 if (auto pSecSequence
= o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(*pSeqAny
))
163 PropertyPairHashMap::iterator
aHashIter(
164 aPropPairHashMap
.find(PropertyPair(rSequenceName
, rPropVal
.Name
)));
165 auto& rSeq
= const_cast<css::uno::Sequence
<css::beans::PropertyValue
>&>(*pSecSequence
);
166 if (aHashIter
!= aPropPairHashMap
.end())
168 rSeq
.getArray()[(*aHashIter
).second
].Value
= rPropVal
.Value
;
172 const sal_Int32 nCount
= pSecSequence
->getLength();
173 rSeq
.realloc(nCount
+ 1);
174 rSeq
.getArray()[nCount
] = rPropVal
;
176 aPropPairHashMap
[PropertyPair(rSequenceName
, rPropVal
.Name
)] = nCount
;
183 void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString
& rPropName
)
185 if ( !aPropSeq
.hasElements() )
188 PropertyHashMap::iterator
aHashIter( aPropHashMap
.find( rPropName
) );
189 if ( aHashIter
== aPropHashMap
.end() )
192 auto pPropSeq
= aPropSeq
.getArray();
193 css::uno::Any
& rSeqAny
= pPropSeq
[(*aHashIter
).second
].Value
;
194 if (auto pSecSequence
195 = o3tl::tryAccess
<css::uno::Sequence
<beans::PropertyValue
>>(rSeqAny
))
197 for (const auto& rPropVal
: *pSecSequence
)
199 auto _aHashIter(aPropPairHashMap
.find(PropertyPair(rPropName
, rPropVal
.Name
)));
200 if (_aHashIter
!= aPropPairHashMap
.end())
201 aPropPairHashMap
.erase(_aHashIter
); // removing property from pair hashmap
204 sal_Int32 nLength
= aPropSeq
.getLength();
207 sal_Int32 nIndex
= (*aHashIter
).second
;
208 if ( nIndex
!= ( nLength
- 1 ) ) // resizing sequence
210 PropertyHashMap::iterator
aHashIter2( aPropHashMap
.find( aPropSeq
[ nLength
- 1 ].Name
) );
211 (*aHashIter2
).second
= nIndex
;
212 pPropSeq
[ nIndex
] = aPropSeq
[ nLength
- 1 ];
214 aPropSeq
.realloc( nLength
- 1 );
216 aPropHashMap
.erase( aHashIter
); // removing property from hashmap
220 SdrCustomShapeGeometryItem::~SdrCustomShapeGeometryItem()
224 bool SdrCustomShapeGeometryItem::operator==( const SfxPoolItem
& rCmp
) const
226 if( !SfxPoolItem::operator==( rCmp
))
228 const SdrCustomShapeGeometryItem
& other
= static_cast<const SdrCustomShapeGeometryItem
&>(rCmp
);
229 // This is called often by SfxItemPool, and comparing uno sequences is relatively slow.
230 // So keep a hash of the sequence and if either of the sequences has a usable hash,
231 // compare using that.
234 if( aHashState
!= other
.aHashState
)
236 if( aHashState
== HashState::Valid
&& aHash
!= other
.aHash
)
239 return aPropSeq
== other
.aPropSeq
;
242 bool SdrCustomShapeGeometryItem::operator<( const SfxPoolItem
& rCmp
) const
244 assert(dynamic_cast<const SdrCustomShapeGeometryItem
*>( &rCmp
));
245 const SdrCustomShapeGeometryItem
& other
= static_cast<const SdrCustomShapeGeometryItem
&>(rCmp
);
246 // Again, try to optimize by checking hashes first (this is operator< for sorting purposes,
247 // so the ordering can be somewhat arbitrary).
250 if( aHashState
!= other
.aHashState
)
251 return aHashState
< other
.aHashState
;
252 if( aHashState
== HashState::Valid
)
253 return aHash
< other
.aHash
;
255 return comphelper::anyLess( css::uno::Any( aPropSeq
),
256 css::uno::Any( other
.aPropSeq
));
259 void SdrCustomShapeGeometryItem::UpdateHash() const
261 if( aHashState
!= HashState::Unknown
)
263 std::optional
< size_t > hash
= comphelper::anyToHash( css::uno::Any( aPropSeq
));
264 if( hash
.has_value())
267 aHashState
= HashState::Valid
;
270 aHashState
= HashState::Unusable
;
273 void SdrCustomShapeGeometryItem::InvalidateHash()
275 aHashState
= HashState::Unknown
;
278 bool SdrCustomShapeGeometryItem::GetPresentation(
279 SfxItemPresentation ePresentation
, MapUnit
/*eCoreMetric*/,
280 MapUnit
/*ePresentationMetric*/, OUString
&rText
, const IntlWrapper
&) const
283 if ( ePresentation
== SfxItemPresentation::Complete
)
288 else if ( ePresentation
== SfxItemPresentation::Nameless
)
293 SdrCustomShapeGeometryItem
* SdrCustomShapeGeometryItem::Clone( SfxItemPool
* /*pPool*/ ) const
295 return new SdrCustomShapeGeometryItem( aPropSeq
);
298 bool SdrCustomShapeGeometryItem::QueryValue( uno::Any
& rVal
, sal_uInt8
/*nMemberId*/ ) const
304 bool SdrCustomShapeGeometryItem::PutValue( const uno::Any
& rVal
, sal_uInt8
/*nMemberId*/ )
306 css::uno::Sequence
< css::beans::PropertyValue
> propSeq
;
307 if ( ! ( rVal
>>= propSeq
) )
310 SetPropSeq( propSeq
);
314 void SdrCustomShapeGeometryItem::SetPropSeq( const css::uno::Sequence
< css::beans::PropertyValue
>& rVal
)
316 if( aPropSeq
== rVal
)
320 aPropHashMap
.clear();
321 aPropPairHashMap
.clear();
322 for ( sal_Int32 i
= 0; i
< aPropSeq
.getLength(); i
++ )
324 const beans::PropertyValue
& rPropVal
= aPropSeq
[ i
];
325 std::pair
<PropertyHashMap::iterator
, bool> const ret(
326 aPropHashMap
.insert(std::make_pair(rPropVal
.Name
, i
)));
327 assert(ret
.second
); // serious bug: duplicate xml attribute exported
330 throw uno::RuntimeException(
331 "CustomShapeGeometry has duplicate property " + rPropVal
.Name
);
333 if (auto rPropSeq
= o3tl::tryAccess
<uno::Sequence
<beans::PropertyValue
>>(
336 for ( sal_Int32 j
= 0; j
< rPropSeq
->getLength(); j
++ )
338 beans::PropertyValue
const & rPropVal2
= (*rPropSeq
)[ j
];
339 aPropPairHashMap
[ PropertyPair( rPropVal
.Name
, rPropVal2
.Name
) ] = j
;
346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */