LibreOffice Module svx (master)  1
customshapeitem.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <o3tl/any.hxx>
24 #include <comphelper/anytohash.hxx>
25 #include <svx/sdasitm.hxx>
26 
27 #include <com/sun/star/beans/PropertyValue.hpp>
28 
29 using namespace ::std;
30 using namespace com::sun::star;
31 
32 
35 {}
36 
37 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem( const uno::Sequence< beans::PropertyValue >& rVal )
39 {
40  SetPropSeq( rVal );
41 }
42 
43 css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rPropName )
44 {
45  css::uno::Any* pRet = nullptr;
46  PropertyHashMap::iterator aHashIter( aPropHashMap.find( rPropName ) );
47  if ( aHashIter != aPropHashMap.end() )
48  pRet = &aPropSeq.getArray()[ (*aHashIter).second ].Value;
49  return pRet;
50 }
51 
52 const css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rPropName ) const
53 {
54  const css::uno::Any* pRet = nullptr;
55  PropertyHashMap::const_iterator aHashIter( aPropHashMap.find( rPropName ) );
56  if ( aHashIter != aPropHashMap.end() )
57  pRet = &aPropSeq[ (*aHashIter).second ].Value;
58  return pRet;
59 }
60 
61 css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rSequenceName, const OUString& rPropName )
62 {
63  css::uno::Any* pRet = nullptr;
64  css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
65  if ( pSeqAny )
66  {
67  if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny) )
68  {
69  PropertyPairHashMap::iterator aHashIter( aPropPairHashMap.find( PropertyPair( rSequenceName, rPropName ) ) );
70  if ( aHashIter != aPropPairHashMap.end() )
71  {
72  pRet = &const_cast<css::uno::Sequence<css::beans::PropertyValue> &>(*rSecSequence).getArray()[ (*aHashIter).second ].Value;
73  }
74  }
75  }
76  return pRet;
77 }
78 
79 const css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rSequenceName, const OUString& rPropName ) const
80 {
81  const css::uno::Any* pRet = nullptr;
82  const css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
83  if ( pSeqAny )
84  {
85  if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny) )
86  {
87  PropertyPairHashMap::const_iterator aHashIter( aPropPairHashMap.find( PropertyPair( rSequenceName, rPropName ) ) );
88  if ( aHashIter != aPropPairHashMap.end() )
89  {
90  pRet = &(*rSecSequence)[ (*aHashIter).second ].Value;
91  }
92  }
93  }
94  return pRet;
95 }
96 
97 void SdrCustomShapeGeometryItem::SetPropertyValue( const css::beans::PropertyValue& rPropVal )
98 {
99  css::uno::Any* pAny = GetPropertyValueByName( rPropVal.Name );
100  if ( pAny )
101  { // property is already available
102  if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pAny) )
103  { // old property is a sequence->each entry has to be removed from the HashPairMap
104  for ( auto const & i : *rSecSequence )
105  {
106  PropertyPairHashMap::iterator aHashIter( aPropPairHashMap.find( PropertyPair( rPropVal.Name, i.Name ) ) );
107  if ( aHashIter != aPropPairHashMap.end() )
108  aPropPairHashMap.erase( aHashIter );
109  }
110  }
111  *pAny = rPropVal.Value;
112  if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pAny) )
113  { // the new property is a sequence->each entry has to be inserted into the HashPairMap
114  for ( sal_Int32 i = 0; i < rSecSequence->getLength(); i++ )
115  {
116  beans::PropertyValue const & rPropVal2 = (*rSecSequence)[ i ];
117  aPropPairHashMap[ PropertyPair( rPropVal.Name, rPropVal2.Name ) ] = i;
118  }
119  }
120  }
121  else
122  { // it's a new property
123  assert(std::none_of(std::cbegin(aPropSeq), std::cend(aPropSeq),
124  [&rPropVal](beans::PropertyValue const& rVal)
125  { return rVal.Name == rPropVal.Name; } ));
126  sal_uInt32 nIndex = aPropSeq.getLength();
127  aPropSeq.realloc( nIndex + 1 );
128  aPropSeq.getArray()[ nIndex ] = rPropVal ;
129 
130  aPropHashMap[ rPropVal.Name ] = nIndex;
131  }
132  InvalidateHash();
133 }
134 
135 void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString& rSequenceName, const css::beans::PropertyValue& rPropVal )
136 {
137  css::uno::Any* pAny = GetPropertyValueByName( rSequenceName, rPropVal.Name );
138  if ( pAny ) // just replacing
139  *pAny = rPropVal.Value;
140  else
141  {
142  css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
143  if( pSeqAny == nullptr )
144  {
145  css::uno::Sequence < beans::PropertyValue > aSeq;
146  beans::PropertyValue aValue;
147  aValue.Name = rSequenceName;
148  aValue.Value <<= aSeq;
149 
150  assert(std::none_of(std::cbegin(aPropSeq), std::cend(aPropSeq),
151  [&rSequenceName](beans::PropertyValue const& rV)
152  { return rV.Name == rSequenceName; } ));
153  sal_uInt32 nIndex = aPropSeq.getLength();
154  aPropSeq.realloc( nIndex + 1 );
155  auto pPropSeq = aPropSeq.getArray();
156  pPropSeq[ nIndex ] = aValue;
157  aPropHashMap[ rSequenceName ] = nIndex;
158 
159  pSeqAny = &pPropSeq[ nIndex ].Value;
160  }
161 
162  if (auto pSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny))
163  {
164  PropertyPairHashMap::iterator aHashIter(
165  aPropPairHashMap.find(PropertyPair(rSequenceName, rPropVal.Name)));
166  auto& rSeq = const_cast<css::uno::Sequence<css::beans::PropertyValue>&>(*pSecSequence);
167  if (aHashIter != aPropPairHashMap.end())
168  {
169  rSeq.getArray()[(*aHashIter).second].Value = rPropVal.Value;
170  }
171  else
172  {
173  const sal_Int32 nCount = pSecSequence->getLength();
174  rSeq.realloc(nCount + 1);
175  rSeq.getArray()[nCount] = rPropVal;
176 
177  aPropPairHashMap[PropertyPair(rSequenceName, rPropVal.Name)] = nCount;
178  }
179  }
180  }
181  InvalidateHash();
182 }
183 
184 void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString& rPropName )
185 {
186  if ( !aPropSeq.hasElements() )
187  return;
188 
189  PropertyHashMap::iterator aHashIter( aPropHashMap.find( rPropName ) );
190  if ( aHashIter == aPropHashMap.end() )
191  return;
192 
193  auto pPropSeq = aPropSeq.getArray();
194  css::uno::Any& rSeqAny = pPropSeq[(*aHashIter).second].Value;
195  if (auto pSecSequence
196  = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(rSeqAny))
197  {
198  for (const auto& rPropVal : *pSecSequence)
199  {
200  auto _aHashIter(aPropPairHashMap.find(PropertyPair(rPropName, rPropVal.Name)));
201  if (_aHashIter != aPropPairHashMap.end())
202  aPropPairHashMap.erase(_aHashIter); // removing property from pair hashmap
203  }
204  }
205  sal_Int32 nLength = aPropSeq.getLength();
206  if ( nLength )
207  {
208  sal_Int32 nIndex = (*aHashIter).second;
209  if ( nIndex != ( nLength - 1 ) ) // resizing sequence
210  {
211  PropertyHashMap::iterator aHashIter2( aPropHashMap.find( aPropSeq[ nLength - 1 ].Name ) );
212  (*aHashIter2).second = nIndex;
213  pPropSeq[ nIndex ] = aPropSeq[ nLength - 1 ];
214  }
215  aPropSeq.realloc( nLength - 1 );
216  }
217  aPropHashMap.erase( aHashIter ); // removing property from hashmap
218  InvalidateHash();
219 }
220 
222 {
223 }
224 
226 {
227  if( !SfxPoolItem::operator==( rCmp ))
228  return false;
229  const SdrCustomShapeGeometryItem& other = static_cast<const SdrCustomShapeGeometryItem&>(rCmp);
230  // This is called often by SfxItemPool, and comparing uno sequences is relatively slow.
231  // So keep a hash of the sequence and if either of the sequences has a usable hash,
232  // compare using that.
233  UpdateHash();
234  other.UpdateHash();
235  if( aHashState != other.aHashState )
236  return false;
237  if( aHashState == HashState::Valid && aHash != other.aHash )
238  return false;
239 
240  return aPropSeq == other.aPropSeq;
241 }
242 
244 {
245  assert(dynamic_cast<const SdrCustomShapeGeometryItem*>( &rCmp ));
246  const SdrCustomShapeGeometryItem& other = static_cast<const SdrCustomShapeGeometryItem&>(rCmp);
247  // Again, try to optimize by checking hashes first (this is operator< for sorting purposes,
248  // so the ordering can be somewhat arbitrary).
249  UpdateHash();
250  other.UpdateHash();
251  if( aHashState != other.aHashState )
252  return aHashState < other.aHashState;
253  if( aHashState == HashState::Valid )
254  return aHash < other.aHash;
255 
256  return comphelper::anyLess( css::uno::makeAny( aPropSeq ),
257  css::uno::makeAny( other.aPropSeq ));
258 }
259 
261 {
262  if( aHashState != HashState::Unknown )
263  return;
264  std::optional< size_t > hash = comphelper::anyToHash( css::uno::makeAny( aPropSeq ));
265  if( hash.has_value())
266  {
267  aHash = *hash;
268  aHashState = HashState::Valid;
269  }
270  else
271  aHashState = HashState::Unusable;
272 }
273 
275 {
276  aHashState = HashState::Unknown;
277 }
278 
280  SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
281  MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
282 {
283  rText += " ";
284  if ( ePresentation == SfxItemPresentation::Complete )
285  {
286  rText = " " + rText;
287  return true;
288  }
289  else if ( ePresentation == SfxItemPresentation::Nameless )
290  return true;
291  return false;
292 }
293 
295 {
296  return new SdrCustomShapeGeometryItem( aPropSeq );
297 }
298 
300 {
301  rVal <<= aPropSeq;
302  return true;
303 }
304 
305 bool SdrCustomShapeGeometryItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
306 {
307  css::uno::Sequence< css::beans::PropertyValue > propSeq;
308  if ( ! ( rVal >>= propSeq ) )
309  return false;
310 
311  SetPropSeq( propSeq );
312  return true;
313 }
314 
315 void SdrCustomShapeGeometryItem::SetPropSeq( const css::uno::Sequence< css::beans::PropertyValue >& rVal )
316 {
317  if( aPropSeq == rVal )
318  return;
319 
320  aPropSeq = rVal;
321  aPropHashMap.clear();
322  aPropPairHashMap.clear();
323  for ( sal_Int32 i = 0; i < aPropSeq.getLength(); i++ )
324  {
325  const beans::PropertyValue& rPropVal = aPropSeq[ i ];
326  std::pair<PropertyHashMap::iterator, bool> const ret(
327  aPropHashMap.insert(std::make_pair(rPropVal.Name, i)));
328  assert(ret.second); // serious bug: duplicate xml attribute exported
329  if (!ret.second)
330  {
331  throw uno::RuntimeException(
332  "CustomShapeGeometry has duplicate property " + rPropVal.Name);
333  }
334  if (auto rPropSeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(
335  rPropVal.Value))
336  {
337  for ( sal_Int32 j = 0; j < rPropSeq->getLength(); j++ )
338  {
339  beans::PropertyValue const & rPropVal2 = (*rPropSeq)[ j ];
340  aPropPairHashMap[ PropertyPair( rPropVal.Name, rPropVal2.Name ) ] = j;
341  }
342  }
343  }
344  InvalidateHash();
345 }
346 
347 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetPropertyValue(const css::beans::PropertyValue &rPropVal)
sal_Int32 nIndex
virtual bool GetPresentation(SfxItemPresentation ePresentation, MapUnit eCoreMetric, MapUnit ePresentationMetric, OUString &rText, const IntlWrapper &) const override
PropertyPairHashMap aPropPairHashMap
Definition: sdasitm.hxx:47
virtual bool QueryValue(css::uno::Any &rVal, sal_uInt8 nMemberId=0) const override
bool anyLess(css::uno::Any const &lhs, css::uno::Any const &rhs)
virtual bool operator==(const SfxPoolItem &) const override
int nCount
std::enable_if< !(detail::IsDerivedReference< T >::value||detail::IsUnoSequenceType< T >::value||std::is_base_of< css::uno::XInterface, T >::value), typename detail::Optional< T >::type >::type tryAccess(css::uno::Any const &any)
std::pair< const OUString, const OUString > PropertyPair
Definition: sdasitm.hxx:36
std::optional< size_t > anyToHash(uno::Any const &value)
SfxItemPresentation
constexpr TypedWhichId< SdrCustomShapeGeometryItem > SDRATTR_CUSTOMSHAPE_GEOMETRY(SDRATTR_CUSTOMSHAPE_FIRST+2)
int i
css::uno::Sequence< css::beans::PropertyValue > aPropSeq
Definition: sdasitm.hxx:49
virtual ~SdrCustomShapeGeometryItem() override
virtual bool operator<(const SfxPoolItem &) const override
PropertyHashMap aPropHashMap
Definition: sdasitm.hxx:46
virtual SdrCustomShapeGeometryItem * Clone(SfxItemPool *pPool=nullptr) const override
virtual bool PutValue(const css::uno::Any &rVal, sal_uInt8 nMemberId) override
unsigned char sal_uInt8
Sequence< sal_Int8 > aSeq
css::uno::Any * GetPropertyValueByName(const OUString &rPropName)
MapUnit
sal_Int32 nLength
void SetPropSeq(const css::uno::Sequence< css::beans::PropertyValue > &rPropSeq)
void ClearPropertyValue(const OUString &rPropertyName)