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>
23 #include <svx/sdasitm.hxx>
24 
25 #include <com/sun/star/beans/PropertyValue.hpp>
26 
27 using namespace ::std;
28 using namespace com::sun::star;
29 
30 
SdrCustomShapeGeometryItem()31 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem()
32 :   SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )
33 {}
34 
SdrCustomShapeGeometryItem(const uno::Sequence<beans::PropertyValue> & rVal)35 SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem( const uno::Sequence< beans::PropertyValue >& rVal )
36 :   SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )
37 {
38     sal_Int32 i, j;
39     aPropSeq = rVal;
40 
41     for ( i = 0; i < aPropSeq.getLength(); i++ )
42     {
43         beans::PropertyValue& rPropVal = aPropSeq[ i ];
44         std::pair<PropertyHashMap::iterator, bool> const ret(
45                 aPropHashMap.insert(std::make_pair(rPropVal.Name, i)));
46         assert(ret.second); // serious bug: duplicate xml attribute exported
47         if (!ret.second)
48         {
49             throw uno::RuntimeException(
50                 "CustomShapeGeometry has duplicate property " + rPropVal.Name);
51         }
52         if (auto rPropSeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(
53                 rPropVal.Value))
54         {
55             for ( j = 0; j < rPropSeq->getLength(); j++ )
56             {
57                 beans::PropertyValue const & rPropVal2 = (*rPropSeq)[ j ];
58                 aPropPairHashMap[ PropertyPair( rPropVal.Name, rPropVal2.Name ) ] = j;
59             }
60         }
61     }
62 }
63 
GetPropertyValueByName(const OUString & rPropName)64 css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rPropName )
65 {
66     css::uno::Any* pRet = nullptr;
67     PropertyHashMap::iterator aHashIter( aPropHashMap.find( rPropName ) );
68     if ( aHashIter != aPropHashMap.end() )
69         pRet = &aPropSeq[ (*aHashIter).second ].Value;
70     return pRet;
71 }
72 
GetPropertyValueByName(const OUString & rPropName) const73 const css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rPropName ) const
74 {
75     const css::uno::Any* pRet = nullptr;
76     PropertyHashMap::const_iterator aHashIter( aPropHashMap.find( rPropName ) );
77     if ( aHashIter != aPropHashMap.end() )
78         pRet = &aPropSeq[ (*aHashIter).second ].Value;
79     return pRet;
80 }
81 
GetPropertyValueByName(const OUString & rSequenceName,const OUString & rPropName)82 css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rSequenceName, const OUString& rPropName )
83 {
84     css::uno::Any* pRet = nullptr;
85     css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
86     if ( pSeqAny )
87     {
88         if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny) )
89         {
90             PropertyPairHashMap::iterator aHashIter( aPropPairHashMap.find( PropertyPair( rSequenceName, rPropName ) ) );
91             if ( aHashIter != aPropPairHashMap.end() )
92             {
93                 pRet = &const_cast<css::uno::Sequence<css::beans::PropertyValue> &>(*rSecSequence)[ (*aHashIter).second ].Value;
94             }
95         }
96     }
97     return pRet;
98 }
99 
GetPropertyValueByName(const OUString & rSequenceName,const OUString & rPropName) const100 const css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rSequenceName, const OUString& rPropName ) const
101 {
102     const css::uno::Any* pRet = nullptr;
103     const css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
104     if ( pSeqAny )
105     {
106         if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny) )
107         {
108             PropertyPairHashMap::const_iterator aHashIter( aPropPairHashMap.find( PropertyPair( rSequenceName, rPropName ) ) );
109             if ( aHashIter != aPropPairHashMap.end() )
110             {
111                 pRet = &(*rSecSequence)[ (*aHashIter).second ].Value;
112             }
113         }
114     }
115     return pRet;
116 }
117 
SetPropertyValue(const css::beans::PropertyValue & rPropVal)118 void SdrCustomShapeGeometryItem::SetPropertyValue( const css::beans::PropertyValue& rPropVal )
119 {
120     css::uno::Any* pAny = GetPropertyValueByName( rPropVal.Name );
121     if ( pAny )
122     {   // property is already available
123         if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pAny) )
124         {   // old property is a sequence->each entry has to be removed from the HashPairMap
125             for ( auto const & i : *rSecSequence )
126             {
127                 PropertyPairHashMap::iterator aHashIter( aPropPairHashMap.find( PropertyPair( rPropVal.Name, i.Name ) ) );
128                 if ( aHashIter != aPropPairHashMap.end() )
129                     aPropPairHashMap.erase( aHashIter );
130             }
131         }
132         *pAny = rPropVal.Value;
133         if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pAny) )
134         {   // the new property is a sequence->each entry has to be inserted into the HashPairMap
135             for ( sal_Int32 i = 0; i < rSecSequence->getLength(); i++ )
136             {
137                 beans::PropertyValue const & rPropVal2 = (*rSecSequence)[ i ];
138                 aPropPairHashMap[ PropertyPair( rPropVal.Name, rPropVal2.Name ) ] = i;
139             }
140         }
141     }
142     else
143     {   // it's a new property
144         assert(std::none_of(aPropSeq.begin(), aPropSeq.end(),
145             [&rPropVal](beans::PropertyValue const& rVal)
146                 { return rVal.Name == rPropVal.Name; } ));
147         sal_uInt32 nIndex = aPropSeq.getLength();
148         aPropSeq.realloc( nIndex + 1 );
149         aPropSeq[ nIndex ] = rPropVal ;
150 
151         aPropHashMap[ rPropVal.Name ] = nIndex;
152     }
153 }
154 
SetPropertyValue(const OUString & rSequenceName,const css::beans::PropertyValue & rPropVal)155 void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString& rSequenceName, const css::beans::PropertyValue& rPropVal )
156 {
157     css::uno::Any* pAny = GetPropertyValueByName( rSequenceName, rPropVal.Name );
158     if ( pAny ) // just replacing
159         *pAny = rPropVal.Value;
160     else
161     {
162         css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
163         if( pSeqAny == nullptr )
164         {
165             css::uno::Sequence < beans::PropertyValue > aSeq;
166             beans::PropertyValue aValue;
167             aValue.Name = rSequenceName;
168             aValue.Value <<= aSeq;
169 
170             assert(std::none_of(aPropSeq.begin(), aPropSeq.end(),
171                 [&rSequenceName](beans::PropertyValue const& rV)
172                     { return rV.Name == rSequenceName; } ));
173             sal_uInt32 nIndex = aPropSeq.getLength();
174             aPropSeq.realloc( nIndex + 1 );
175             aPropSeq[ nIndex ] = aValue;
176             aPropHashMap[ rSequenceName ] = nIndex;
177 
178             pSeqAny = &aPropSeq[ nIndex ].Value;
179         }
180 
181         if (auto pSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny))
182         {
183             PropertyPairHashMap::iterator aHashIter(
184                 aPropPairHashMap.find(PropertyPair(rSequenceName, rPropVal.Name)));
185             auto& rSeq = const_cast<css::uno::Sequence<css::beans::PropertyValue>&>(*pSecSequence);
186             if (aHashIter != aPropPairHashMap.end())
187             {
188                 rSeq[(*aHashIter).second].Value = rPropVal.Value;
189             }
190             else
191             {
192                 const sal_Int32 nCount = pSecSequence->getLength();
193                 rSeq.realloc(nCount + 1);
194                 rSeq[nCount] = rPropVal;
195 
196                 aPropPairHashMap[PropertyPair(rSequenceName, rPropVal.Name)] = nCount;
197             }
198         }
199     }
200 }
201 
ClearPropertyValue(const OUString & rPropName)202 void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString& rPropName )
203 {
204     if ( !aPropSeq.hasElements() )
205         return;
206 
207     PropertyHashMap::iterator aHashIter( aPropHashMap.find( rPropName ) );
208     if ( aHashIter == aPropHashMap.end() )
209         return;
210 
211     css::uno::Any& rSeqAny = aPropSeq[(*aHashIter).second].Value;
212     if (auto pSecSequence
213         = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(rSeqAny))
214     {
215         for (const auto& rPropVal : *pSecSequence)
216         {
217             auto _aHashIter(aPropPairHashMap.find(PropertyPair(rPropName, rPropVal.Name)));
218             if (_aHashIter != aPropPairHashMap.end())
219                 aPropPairHashMap.erase(_aHashIter); // removing property from pair hashmap
220         }
221     }
222     sal_Int32 nLength = aPropSeq.getLength();
223     if ( nLength )
224     {
225         sal_Int32 nIndex  = (*aHashIter).second;
226         if ( nIndex != ( nLength - 1 ) )                        // resizing sequence
227         {
228             PropertyHashMap::iterator aHashIter2( aPropHashMap.find( aPropSeq[ nLength - 1 ].Name ) );
229             (*aHashIter2).second = nIndex;
230             aPropSeq[ nIndex ] = aPropSeq[ nLength - 1 ];
231         }
232         aPropSeq.realloc( nLength - 1 );
233     }
234     aPropHashMap.erase( aHashIter );                            // removing property from hashmap
235 }
236 
~SdrCustomShapeGeometryItem()237 SdrCustomShapeGeometryItem::~SdrCustomShapeGeometryItem()
238 {
239 }
operator ==(const SfxPoolItem & rCmp) const240 bool SdrCustomShapeGeometryItem::operator==( const SfxPoolItem& rCmp ) const
241 {
242     bool bRet = SfxPoolItem::operator==( rCmp );
243     if ( bRet )
244         bRet = static_cast<const SdrCustomShapeGeometryItem&>(rCmp).aPropSeq == aPropSeq;
245     return bRet;
246 }
247 
GetPresentation(SfxItemPresentation ePresentation,MapUnit,MapUnit,OUString & rText,const IntlWrapper &) const248 bool SdrCustomShapeGeometryItem::GetPresentation(
249     SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
250     MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
251 {
252     rText += " ";
253     if ( ePresentation == SfxItemPresentation::Complete )
254     {
255         rText = " " + rText;
256         return true;
257     }
258     else if ( ePresentation == SfxItemPresentation::Nameless )
259         return true;
260     return false;
261 }
262 
Clone(SfxItemPool *) const263 SdrCustomShapeGeometryItem* SdrCustomShapeGeometryItem::Clone( SfxItemPool * /*pPool*/ ) const
264 {
265     return new SdrCustomShapeGeometryItem( aPropSeq );
266 }
267 
QueryValue(uno::Any & rVal,sal_uInt8) const268 bool SdrCustomShapeGeometryItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
269 {
270     rVal <<= aPropSeq;
271     return true;
272 }
273 
PutValue(const uno::Any & rVal,sal_uInt8)274 bool SdrCustomShapeGeometryItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
275 {
276     if ( ! ( rVal >>= aPropSeq ) )
277         return false;
278 
279     for (sal_Int32 i = 0; i < aPropSeq.getLength(); ++i)
280     {
281         const auto& rName = aPropSeq[i].Name;
282         bool isDuplicated = std::any_of(std::next(aPropSeq.begin(), i + 1), aPropSeq.end(),
283             [&rName](const css::beans::PropertyValue& rProp) { return rProp.Name == rName; });
284         if (isDuplicated)
285         {
286             assert(false); // serious bug: duplicate xml attribute exported
287             OUString const name(aPropSeq[i].Name);
288             aPropSeq.realloc(0);
289             throw uno::RuntimeException(
290                 "CustomShapeGeometry has duplicate property " + name);
291         }
292     }
293     return true;
294 }
295 
296 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
297