Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgspropertycollection.cpp
3 : : -------------------------
4 : : Date : January 2017
5 : : Copyright : (C) 2017 by Nyall Dawson
6 : : Email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #include "qgspropertycollection.h"
17 : : #include "qgsproperty.h"
18 : : #include "qgsxmlutils.h"
19 : :
20 : : //
21 : : // QgsAbstractPropertyCollection
22 : : //
23 : :
24 : 2499 : QgsAbstractPropertyCollection::QgsAbstractPropertyCollection( const QString &name )
25 : 2499 : : mName( name )
26 : 2499 : {
27 : :
28 : 2499 : }
29 : :
30 : 0 : QDateTime QgsAbstractPropertyCollection::valueAsDateTime( int key, const QgsExpressionContext &context, const QDateTime &defaultDateTime, bool *ok ) const
31 : : {
32 : 0 : if ( ok )
33 : 0 : *ok = false;
34 : :
35 : 0 : QgsProperty prop = property( key );
36 : 0 : if ( !prop || !prop.isActive() )
37 : 0 : return defaultDateTime;
38 : :
39 : 0 : return prop.valueAsDateTime( context, defaultDateTime, ok );
40 : 0 : }
41 : :
42 : 0 : QString QgsAbstractPropertyCollection::valueAsString( int key, const QgsExpressionContext &context, const QString &defaultString, bool *ok ) const
43 : : {
44 : 0 : if ( ok )
45 : 0 : *ok = false;
46 : :
47 : 0 : QgsProperty prop = property( key );
48 : 0 : if ( !prop || !prop.isActive() )
49 : 0 : return defaultString;
50 : :
51 : 0 : return prop.valueAsString( context, defaultString, ok );
52 : 0 : }
53 : :
54 : 0 : QColor QgsAbstractPropertyCollection::valueAsColor( int key, const QgsExpressionContext &context, const QColor &defaultColor, bool *ok ) const
55 : : {
56 : 0 : if ( ok )
57 : 0 : *ok = false;
58 : :
59 : 0 : QgsProperty prop = property( key );
60 : 0 : if ( !prop || !prop.isActive() )
61 : 0 : return defaultColor;
62 : :
63 : 0 : return prop.valueAsColor( context, defaultColor, ok );
64 : 0 : }
65 : :
66 : 0 : double QgsAbstractPropertyCollection::valueAsDouble( int key, const QgsExpressionContext &context, double defaultValue, bool *ok ) const
67 : : {
68 : 0 : if ( ok )
69 : 0 : *ok = false;
70 : 0 : QgsProperty prop = property( key );
71 : 0 : if ( !prop || !prop.isActive() )
72 : 0 : return defaultValue;
73 : :
74 : 0 : return prop.valueAsDouble( context, defaultValue, ok );
75 : 0 : }
76 : :
77 : 0 : int QgsAbstractPropertyCollection::valueAsInt( int key, const QgsExpressionContext &context, int defaultValue, bool *ok ) const
78 : : {
79 : 0 : if ( ok )
80 : 0 : *ok = false;
81 : 0 : QgsProperty prop = property( key );
82 : 0 : if ( !prop || !prop.isActive() )
83 : 0 : return defaultValue;
84 : :
85 : 0 : return prop.valueAsInt( context, defaultValue, ok );
86 : 0 : }
87 : :
88 : 0 : bool QgsAbstractPropertyCollection::valueAsBool( int key, const QgsExpressionContext &context, bool defaultValue, bool *ok ) const
89 : : {
90 : 0 : if ( ok )
91 : 0 : *ok = false;
92 : 0 : QgsProperty prop = property( key );
93 : 0 : if ( !prop || !prop.isActive() )
94 : 0 : return defaultValue;
95 : :
96 : 0 : return prop.valueAsBool( context, defaultValue, ok );
97 : 0 : }
98 : :
99 : 0 : bool QgsAbstractPropertyCollection::writeXml( QDomElement &collectionElem, const QgsPropertiesDefinition &definitions ) const
100 : : {
101 : 0 : QVariant collection = toVariant( definitions );
102 : 0 : QDomDocument doc = collectionElem.ownerDocument();
103 : 0 : QDomElement element = QgsXmlUtils::writeVariant( collection, doc );
104 : 0 : collectionElem.appendChild( element );
105 : : return true;
106 : 0 : }
107 : :
108 : 1570 : bool QgsAbstractPropertyCollection::readXml( const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions )
109 : : {
110 : 1570 : QVariant collection = QgsXmlUtils::readVariant( collectionElem.firstChild().toElement() );
111 : 1570 : return loadVariant( collection.toMap(), definitions );
112 : 1570 : }
113 : :
114 : :
115 : :
116 : : //
117 : : // QgsPropertyCollection
118 : : //
119 : :
120 : 2499 : QgsPropertyCollection::QgsPropertyCollection( const QString &name )
121 : 2499 : : QgsAbstractPropertyCollection( name )
122 : 7497 : {}
123 : :
124 : 0 : QgsPropertyCollection::QgsPropertyCollection( const QgsPropertyCollection &other )
125 : 0 : : QgsAbstractPropertyCollection( other )
126 : 0 : , mProperties( other.mProperties )
127 : 0 : , mDirty( other.mDirty )
128 : 0 : , mHasActiveProperties( other.mHasActiveProperties )
129 : 0 : , mHasDynamicProperties( other.mHasDynamicProperties )
130 : 0 : , mCount( other.mCount )
131 : 0 : {
132 : 0 : mProperties.detach();
133 : 0 : }
134 : :
135 : 0 : QgsPropertyCollection &QgsPropertyCollection::operator=( const QgsPropertyCollection &other )
136 : : {
137 : 0 : QgsAbstractPropertyCollection::operator=( other );
138 : 0 : mProperties = other.mProperties;
139 : 0 : mProperties.detach();
140 : 0 : mDirty = other.mDirty;
141 : 0 : mHasActiveProperties = other.mHasActiveProperties;
142 : 0 : mHasDynamicProperties = other.mHasDynamicProperties;
143 : 0 : mCount = other.mCount;
144 : 0 : return *this;
145 : : }
146 : :
147 : 0 : bool QgsPropertyCollection::operator==( const QgsPropertyCollection &other ) const
148 : : {
149 : 0 : return mProperties == other.mProperties;
150 : : }
151 : :
152 : 0 : bool QgsPropertyCollection::operator!=( const QgsPropertyCollection &other ) const
153 : : {
154 : 0 : return !( *this == other );
155 : : }
156 : :
157 : 0 : int QgsPropertyCollection::count() const
158 : : {
159 : 0 : if ( !mDirty )
160 : 0 : return mCount;
161 : :
162 : 0 : rescan();
163 : 0 : return mCount;
164 : 0 : }
165 : :
166 : 0 : QSet<int> QgsPropertyCollection::propertyKeys() const
167 : : {
168 : 0 : QSet<int> keys;
169 : 0 : QHash<int, QgsProperty>::const_iterator it = mProperties.constBegin();
170 : 0 : for ( ; it != mProperties.constEnd(); ++it )
171 : : {
172 : 0 : if ( it.value() )
173 : 0 : keys.insert( it.key() );
174 : 0 : }
175 : 0 : return keys;
176 : 0 : }
177 : :
178 : 1570 : void QgsPropertyCollection::clear()
179 : : {
180 : 1570 : mProperties.clear();
181 : 1570 : mDirty = false;
182 : 1570 : mHasActiveProperties = false;
183 : 1570 : mHasDynamicProperties = false;
184 : 1570 : mCount = 0;
185 : 1570 : }
186 : :
187 : 0 : void QgsPropertyCollection::setProperty( int key, const QgsProperty &property )
188 : : {
189 : 0 : if ( property )
190 : 0 : mProperties.insert( key, property );
191 : : else
192 : 0 : mProperties.remove( key );
193 : :
194 : 0 : mDirty = true;
195 : 0 : }
196 : :
197 : 0 : void QgsPropertyCollection::setProperty( int key, const QVariant &value )
198 : : {
199 : 0 : mProperties.insert( key, QgsProperty::fromValue( value ) );
200 : 0 : mDirty = true;
201 : 0 : }
202 : :
203 : 0 : bool QgsPropertyCollection::hasProperty( int key ) const
204 : : {
205 : 0 : if ( mProperties.isEmpty() )
206 : 0 : return false;
207 : :
208 : 0 : auto it = mProperties.constFind( key );
209 : 0 : if ( it != mProperties.constEnd() )
210 : 0 : return ( *it );
211 : 0 : return false;
212 : 0 : }
213 : :
214 : 0 : QgsProperty QgsPropertyCollection::property( int key ) const
215 : : {
216 : 0 : if ( mProperties.isEmpty() )
217 : 0 : return QgsProperty();
218 : :
219 : 0 : return mProperties.value( key );
220 : 0 : }
221 : :
222 : 0 : QgsProperty &QgsPropertyCollection::property( int key )
223 : : {
224 : 0 : mDirty = true;
225 : 0 : return mProperties[ key ];
226 : : }
227 : :
228 : 0 : QVariant QgsPropertyCollection::value( int key, const QgsExpressionContext &context, const QVariant &defaultValue ) const
229 : : {
230 : 0 : if ( mProperties.isEmpty() )
231 : 0 : return defaultValue;
232 : :
233 : 0 : QgsProperty prop = mProperties.value( key );
234 : 0 : if ( !prop || !prop.isActive() )
235 : 0 : return defaultValue;
236 : :
237 : 0 : return prop.value( context, defaultValue );
238 : 0 : }
239 : :
240 : 0 : bool QgsPropertyCollection::prepare( const QgsExpressionContext &context ) const
241 : : {
242 : 0 : bool result = true;
243 : 0 : QHash<int, QgsProperty>::const_iterator it = mProperties.constBegin();
244 : 0 : for ( ; it != mProperties.constEnd(); ++it )
245 : : {
246 : 0 : if ( !it.value().isActive() )
247 : 0 : continue;
248 : :
249 : 0 : result = result && it.value().prepare( context );
250 : 0 : }
251 : 0 : return result;
252 : : }
253 : :
254 : 0 : QSet< QString > QgsPropertyCollection::referencedFields( const QgsExpressionContext &context, bool ignoreContext ) const
255 : : {
256 : 0 : QSet< QString > cols;
257 : 0 : QHash<int, QgsProperty>::const_iterator it = mProperties.constBegin();
258 : 0 : for ( ; it != mProperties.constEnd(); ++it )
259 : : {
260 : 0 : if ( !it.value().isActive() )
261 : 0 : continue;
262 : :
263 : 0 : cols.unite( it.value().referencedFields( context, ignoreContext ) );
264 : 0 : }
265 : 0 : return cols;
266 : 0 : }
267 : :
268 : 0 : bool QgsPropertyCollection::isActive( int key ) const
269 : : {
270 : 0 : if ( mProperties.isEmpty() )
271 : 0 : return false;
272 : :
273 : 0 : auto it = mProperties.constFind( key );
274 : 0 : if ( it != mProperties.constEnd() )
275 : 0 : return ( *it ).isActive();
276 : 0 : return false;
277 : 0 : }
278 : :
279 : 0 : void QgsPropertyCollection::rescan() const
280 : : {
281 : 0 : mHasActiveProperties = false;
282 : 0 : mHasDynamicProperties = false;
283 : 0 : mCount = 0;
284 : 0 : if ( !mProperties.isEmpty() )
285 : : {
286 : 0 : QHash<int, QgsProperty>::const_iterator it = mProperties.constBegin();
287 : 0 : for ( ; it != mProperties.constEnd(); ++it )
288 : : {
289 : 0 : if ( it.value() )
290 : 0 : mCount++;
291 : 0 : if ( it.value().isActive() )
292 : : {
293 : 0 : mHasActiveProperties = true;
294 : 0 : if ( it.value().propertyType() != QgsProperty::StaticProperty )
295 : : {
296 : 0 : mHasDynamicProperties = true;
297 : 0 : }
298 : 0 : }
299 : 0 : }
300 : 0 : }
301 : 0 : mDirty = false;
302 : 0 : }
303 : :
304 : 0 : bool QgsPropertyCollection::hasActiveProperties() const
305 : : {
306 : 0 : if ( mDirty )
307 : 0 : rescan();
308 : :
309 : 0 : return mHasActiveProperties;
310 : : }
311 : :
312 : 0 : bool QgsPropertyCollection::hasDynamicProperties() const
313 : : {
314 : 0 : if ( mDirty )
315 : 0 : rescan();
316 : :
317 : 0 : return mHasDynamicProperties;
318 : : }
319 : :
320 : 0 : QVariant QgsPropertyCollection::toVariant( const QgsPropertiesDefinition &definitions ) const
321 : : {
322 : 0 : QVariantMap collection;
323 : :
324 : 0 : collection.insert( QStringLiteral( "name" ), name() );
325 : 0 : collection.insert( QStringLiteral( "type" ), QStringLiteral( "collection" ) );
326 : :
327 : 0 : QVariantMap properties;
328 : :
329 : 0 : QHash<int, QgsProperty>::const_iterator it = mProperties.constBegin();
330 : 0 : for ( ; it != mProperties.constEnd(); ++it )
331 : : {
332 : 0 : if ( it.value() )
333 : : {
334 : 0 : properties.insert( definitions.value( it.key() ).name(), it.value().toVariant() );
335 : 0 : }
336 : 0 : }
337 : 0 : collection.insert( QStringLiteral( "properties" ), properties );
338 : 0 : return collection;
339 : 0 : }
340 : :
341 : 1570 : bool QgsPropertyCollection::loadVariant( const QVariant &collection, const QgsPropertiesDefinition &definitions )
342 : : {
343 : 1570 : clear();
344 : :
345 : 1570 : QVariantMap collectionMap = collection.toMap();
346 : :
347 : 3140 : setName( collectionMap.value( QStringLiteral( "name" ) ).toString() );
348 : :
349 : 1570 : mCount = 0;
350 : 3140 : QVariantMap properties = collectionMap.value( QStringLiteral( "properties" ) ).toMap();
351 : 1570 : for ( auto propertyIterator = properties.constBegin(); propertyIterator != properties.constEnd(); ++propertyIterator )
352 : : {
353 : : // match name to int key
354 : 0 : int key = -1;
355 : 0 : QgsPropertiesDefinition::const_iterator it = definitions.constBegin();
356 : 0 : for ( ; it != definitions.constEnd(); ++it )
357 : : {
358 : 0 : if ( it->name() == propertyIterator.key() )
359 : : {
360 : 0 : key = it.key();
361 : 0 : break;
362 : : }
363 : 0 : }
364 : :
365 : 0 : if ( key < 0 )
366 : 0 : continue;
367 : :
368 : 0 : QgsProperty prop;
369 : 0 : prop.loadVariant( propertyIterator.value() );
370 : 0 : mProperties.insert( key, prop );
371 : :
372 : 0 : mCount++;
373 : :
374 : 0 : mHasActiveProperties = mHasActiveProperties || prop.isActive();
375 : 0 : mHasDynamicProperties = mHasDynamicProperties ||
376 : 0 : ( prop.isActive() &&
377 : 0 : ( prop.propertyType() == QgsProperty::FieldBasedProperty ||
378 : 0 : prop.propertyType() == QgsProperty::ExpressionBasedProperty ) );
379 : 0 : }
380 : : return true;
381 : 1570 : }
382 : :
383 : : //
384 : : // QgsPropertyCollectionStack
385 : : //
386 : :
387 : 0 : QgsPropertyCollectionStack::~QgsPropertyCollectionStack()
388 : 0 : {
389 : 0 : clear();
390 : 2499 : }
391 : 2499 :
392 : 2499 : QgsPropertyCollectionStack::QgsPropertyCollectionStack( const QgsPropertyCollectionStack &other )
393 : 2499 : : QgsAbstractPropertyCollection( other ), mStack()
394 : 0 : {
395 : 0 : clear();
396 : :
397 : 0 : for ( QgsPropertyCollection *collection : std::as_const( other.mStack ) )
398 : : {
399 : 0 : mStack << new QgsPropertyCollection( *collection );
400 : : }
401 : 0 : }
402 : :
403 : 0 : QgsPropertyCollectionStack &QgsPropertyCollectionStack::operator=( const QgsPropertyCollectionStack &other )
404 : : {
405 : 0 : setName( other.name() );
406 : 0 : clear();
407 : :
408 : 0 : for ( QgsPropertyCollection *collection : std::as_const( other.mStack ) )
409 : : {
410 : 0 : mStack << new QgsPropertyCollection( *collection );
411 : : }
412 : :
413 : 0 : return *this;
414 : 0 : }
415 : :
416 : 0 : int QgsPropertyCollectionStack::count() const
417 : : {
418 : 0 : return mStack.size();
419 : : }
420 : :
421 : 0 : void QgsPropertyCollectionStack::clear()
422 : : {
423 : 0 : qDeleteAll( mStack );
424 : 0 : mStack.clear();
425 : 0 : }
426 : :
427 : 0 : void QgsPropertyCollectionStack::appendCollection( QgsPropertyCollection *collection )
428 : : {
429 : 0 : mStack.append( collection );
430 : 0 : }
431 : :
432 : 0 : QgsPropertyCollection *QgsPropertyCollectionStack::at( int index )
433 : : {
434 : 0 : return mStack.value( index );
435 : : }
436 : :
437 : 0 : const QgsPropertyCollection *QgsPropertyCollectionStack::at( int index ) const
438 : : {
439 : 0 : return mStack.value( index );
440 : : }
441 : :
442 : 0 : QgsPropertyCollection *QgsPropertyCollectionStack::collection( const QString &name )
443 : : {
444 : 0 : const auto constMStack = mStack;
445 : 0 : for ( QgsPropertyCollection *collection : constMStack )
446 : : {
447 : 0 : if ( collection->name() == name )
448 : 0 : return collection;
449 : : }
450 : 0 : return nullptr;
451 : 0 : }
452 : :
453 : 0 : bool QgsPropertyCollectionStack::hasActiveProperties() const
454 : : {
455 : 0 : const auto constMStack = mStack;
456 : 0 : for ( const QgsPropertyCollection *collection : constMStack )
457 : : {
458 : 0 : if ( collection->hasActiveProperties() )
459 : 0 : return true;
460 : : }
461 : 0 : return false;
462 : 0 : }
463 : :
464 : 0 : bool QgsPropertyCollectionStack::hasDynamicProperties() const
465 : : {
466 : 0 : const auto constMStack = mStack;
467 : 0 : for ( const QgsPropertyCollection *collection : constMStack )
468 : : {
469 : 0 : if ( collection->hasDynamicProperties() )
470 : 0 : return true;
471 : : }
472 : 0 : return false;
473 : 0 : }
474 : :
475 : 0 : bool QgsPropertyCollectionStack::isActive( int key ) const
476 : : {
477 : 0 : return static_cast< bool >( property( key ) );
478 : 0 : }
479 : :
480 : 0 : QgsProperty QgsPropertyCollectionStack::property( int key ) const
481 : : {
482 : : //loop through stack looking for last active matching property
483 : 0 : for ( int i = mStack.size() - 1; i >= 0; --i )
484 : : {
485 : 0 : const QgsPropertyCollection *collection = mStack.at( i );
486 : 0 : QgsProperty property = collection->property( key );
487 : 0 : if ( property && property.isActive() )
488 : : {
489 : 0 : return property;
490 : : }
491 : 0 : }
492 : : //not found
493 : 0 : return QgsProperty();
494 : 0 : }
495 : :
496 : :
497 : 0 : QVariant QgsPropertyCollectionStack::value( int key, const QgsExpressionContext &context, const QVariant &defaultValue ) const
498 : : {
499 : 0 : QgsProperty p = property( key );
500 : 0 : if ( !p )
501 : : {
502 : 0 : return defaultValue;
503 : : }
504 : 0 : return p.value( context, defaultValue );
505 : 0 : }
506 : :
507 : 0 : QSet< QString > QgsPropertyCollectionStack::referencedFields( const QgsExpressionContext &context, bool ignoreContext ) const
508 : : {
509 : 0 : QSet< QString > cols;
510 : 0 : const auto constMStack = mStack;
511 : 0 : for ( QgsPropertyCollection *collection : constMStack )
512 : : {
513 : 0 : cols.unite( collection->referencedFields( context, ignoreContext ) );
514 : : }
515 : 0 : return cols;
516 : 0 : }
517 : :
518 : 0 : bool QgsPropertyCollectionStack::prepare( const QgsExpressionContext &context ) const
519 : : {
520 : 0 : bool result = true;
521 : 0 : const auto constMStack = mStack;
522 : 0 : for ( QgsPropertyCollection *collection : constMStack )
523 : : {
524 : 0 : result = result && collection->prepare( context );
525 : : }
526 : 0 : return result;
527 : 0 : }
528 : :
529 : 0 : QSet<int> QgsPropertyCollectionStack::propertyKeys() const
530 : : {
531 : 0 : QSet<int> keys;
532 : 0 : const auto constMStack = mStack;
533 : 0 : for ( QgsPropertyCollection *collection : constMStack )
534 : : {
535 : 0 : keys.unite( collection->propertyKeys() );
536 : : }
537 : 0 : return keys;
538 : 0 : }
539 : :
540 : 0 : bool QgsPropertyCollectionStack::hasProperty( int key ) const
541 : : {
542 : 0 : const auto constMStack = mStack;
543 : 0 : for ( QgsPropertyCollection *collection : constMStack )
544 : : {
545 : 0 : if ( collection->hasProperty( key ) )
546 : 0 : return true;
547 : : }
548 : 0 : return false;
549 : 0 : }
550 : :
551 : 0 : QVariant QgsPropertyCollectionStack::toVariant( const QgsPropertiesDefinition &definitions ) const
552 : : {
553 : 0 : QVariantMap collection;
554 : 0 : collection.insert( QStringLiteral( "type" ), QStringLiteral( "stack" ) );
555 : 0 : collection.insert( QStringLiteral( "name" ), name() );
556 : :
557 : 0 : QVariantList properties;
558 : :
559 : 0 : const auto constMStack = mStack;
560 : 0 : for ( QgsPropertyCollection *child : constMStack )
561 : : {
562 : 0 : properties.append( child->toVariant( definitions ) );
563 : : }
564 : :
565 : 0 : collection.insert( QStringLiteral( "properties" ), properties );
566 : :
567 : 0 : return collection;
568 : 0 : }
569 : :
570 : 0 : bool QgsPropertyCollectionStack::loadVariant( const QVariant &collection, const QgsPropertiesDefinition &definitions )
571 : : {
572 : 0 : clear();
573 : :
574 : 0 : QVariantMap collectionMap = collection.toMap();
575 : :
576 : 0 : setName( collectionMap.value( QStringLiteral( "name" ) ).toString() );
577 : :
578 : 0 : QVariantList properties = collectionMap.value( QStringLiteral( "properties" ) ).toList();
579 : :
580 : 0 : const auto constProperties = properties;
581 : 0 : for ( const QVariant &property : constProperties )
582 : : {
583 : 0 : QgsPropertyCollection *propertyCollection = new QgsPropertyCollection();
584 : 0 : propertyCollection->loadVariant( property.toMap(), definitions );
585 : 0 : mStack.append( propertyCollection );
586 : : }
587 : :
588 : : return true;
589 : 0 : }
|