Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsproject.cpp - description
3 : : -------------------
4 : : begin : February 24, 2005
5 : : copyright : (C) 2005 by Mark Coletti
6 : : email : mcoletti at gmail.com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsprojectproperty.h"
19 : : #include "qgslogger.h"
20 : : #include "qgis.h"
21 : : #include "qgsmessagelog.h"
22 : :
23 : : #include <QDomDocument>
24 : : #include <QStringList>
25 : :
26 : 71 : QgsProjectProperty::QgsProjectProperty() //NOLINT
27 : 71 : {
28 : 71 : }
29 : :
30 : 0 : void QgsProjectPropertyValue::dump( int tabs ) const
31 : : {
32 : : Q_UNUSED( tabs )
33 : : #ifdef QGISDEBUG
34 : :
35 : : QString tabString;
36 : : tabString.fill( '\t', tabs );
37 : :
38 : : if ( QVariant::StringList == mValue.type() )
39 : : {
40 : : const QStringList sl = mValue.toStringList();
41 : :
42 : : for ( const auto &string : sl )
43 : : {
44 : : QgsDebugMsgLevel( QStringLiteral( "%1[%2] " ).arg( tabString, string ), 4 );
45 : : }
46 : : }
47 : : else
48 : : {
49 : : QgsDebugMsgLevel( QStringLiteral( "%1%2" ).arg( tabString, mValue.toString() ), 4 );
50 : : }
51 : : #endif
52 : 0 : }
53 : :
54 : 0 : bool QgsProjectPropertyValue::readXml( const QDomNode &keyNode )
55 : : {
56 : : // this *should* be a Dom element node
57 : 0 : QDomElement subkeyElement = keyNode.toElement();
58 : :
59 : : // get the type so that we can properly parse the key value
60 : 0 : QString typeString = subkeyElement.attribute( QStringLiteral( "type" ) );
61 : :
62 : 0 : if ( typeString.isNull() )
63 : : {
64 : 0 : QgsDebugMsg( QStringLiteral( "null ``type'' attribute for %1" ).arg( keyNode.nodeName() ) );
65 : :
66 : 0 : return false;
67 : : }
68 : :
69 : : // the values come in as strings; we need to restore them to their
70 : : // original values *and* types
71 : 0 : mValue.clear();
72 : :
73 : : // get the type associated with the value first
74 : 0 : QVariant::Type type = QVariant::nameToType( typeString.toLocal8Bit().constData() );
75 : :
76 : : // This huge switch is left-over from an earlier incarnation of
77 : : // QgsProject where there was a fine level of granularity for value
78 : : // types. The current interface, borrowed from QgsSettings, supports a
79 : : // very small sub-set of these types. However, I've left all the
80 : : // other types just in case the interface is expanded to include these
81 : : // other types.
82 : :
83 : 0 : switch ( type )
84 : : {
85 : : case QVariant::Invalid:
86 : 0 : QgsDebugMsg( QStringLiteral( "invalid value type %1 .. " ).arg( typeString ) );
87 : 0 : return false;
88 : :
89 : : case QVariant::Map:
90 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Map" ) );
91 : 0 : return false;
92 : :
93 : : case QVariant::List:
94 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::List" ) );
95 : 0 : return false;
96 : :
97 : : case QVariant::String:
98 : 0 : mValue = subkeyElement.text(); // no translating necessary
99 : 0 : break;
100 : :
101 : : case QVariant::StringList:
102 : : {
103 : 0 : int i = 0;
104 : 0 : QDomNodeList values = keyNode.childNodes();
105 : :
106 : : // all the QStringList values will be inside <value> elements
107 : 0 : QStringList valueStringList;
108 : :
109 : 0 : while ( i < values.count() )
110 : : {
111 : 0 : if ( "value" == values.item( i ).nodeName() )
112 : : {
113 : : // <value>s have only one element, which contains actual string value
114 : 0 : valueStringList.append( values.item( i ).firstChild().nodeValue() );
115 : 0 : }
116 : : else
117 : : {
118 : 0 : QgsDebugMsg( QStringLiteral( "non <value> element ``%1'' in string list" ).arg( values.item( i ).nodeName() ) );
119 : : }
120 : :
121 : 0 : ++i;
122 : : }
123 : :
124 : 0 : mValue = valueStringList;
125 : : break;
126 : 0 : }
127 : :
128 : : case QVariant::Font:
129 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Font" ) );
130 : 0 : return false;
131 : :
132 : : case QVariant::Pixmap:
133 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Pixmap" ) );
134 : 0 : return false;
135 : :
136 : : case QVariant::Brush:
137 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Brush" ) );
138 : 0 : return false;
139 : :
140 : : case QVariant::Rect:
141 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Rect" ) );
142 : 0 : return false;
143 : :
144 : : case QVariant::Size:
145 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Size" ) );
146 : 0 : return false;
147 : :
148 : : case QVariant::Color:
149 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Color" ) );
150 : 0 : return false;
151 : :
152 : : case QVariant::Palette:
153 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Palette" ) );
154 : 0 : return false;
155 : :
156 : : case QVariant::Point:
157 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Point" ) );
158 : 0 : return false;
159 : :
160 : : case QVariant::Image:
161 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Image" ) );
162 : 0 : return false;
163 : :
164 : : case QVariant::Int:
165 : 0 : mValue = QVariant( subkeyElement.text() ).toInt();
166 : 0 : break;
167 : :
168 : : case QVariant::UInt:
169 : 0 : mValue = QVariant( subkeyElement.text() ).toUInt();
170 : 0 : break;
171 : :
172 : : case QVariant::Bool:
173 : 0 : mValue = QVariant( subkeyElement.text() ).toBool();
174 : 0 : break;
175 : :
176 : : case QVariant::Double:
177 : 0 : mValue = QVariant( subkeyElement.text() ).toDouble();
178 : 0 : break;
179 : :
180 : : case QVariant::ByteArray:
181 : 0 : mValue = QVariant( subkeyElement.text() ).toByteArray();
182 : 0 : break;
183 : :
184 : : case QVariant::Polygon:
185 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Polygon" ) );
186 : 0 : return false;
187 : :
188 : : case QVariant::Region:
189 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Region" ) );
190 : 0 : return false;
191 : :
192 : : case QVariant::Bitmap:
193 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Bitmap" ) );
194 : 0 : return false;
195 : :
196 : : case QVariant::Cursor:
197 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Cursor" ) );
198 : 0 : return false;
199 : :
200 : : case QVariant::BitArray :
201 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::BitArray" ) );
202 : 0 : return false;
203 : :
204 : : case QVariant::KeySequence :
205 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::KeySequence" ) );
206 : 0 : return false;
207 : :
208 : : case QVariant::Pen :
209 : 0 : QgsDebugMsg( QStringLiteral( "no support for QVariant::Pen" ) );
210 : 0 : return false;
211 : :
212 : : #if 0 // Currently unsupported variant types
213 : : case QVariant::LongLong :
214 : : value_ = QVariant( subkeyElement.text() ).toLongLong();
215 : : break;
216 : :
217 : : case QVariant::ULongLong :
218 : : value_ = QVariant( subkeyElement.text() ).toULongLong();
219 : : break;
220 : : #endif
221 : : default :
222 : 0 : QgsDebugMsg( QStringLiteral( "unsupported value type %1 .. not properly translated to QVariant" ).arg( typeString ) );
223 : 0 : }
224 : :
225 : 0 : return true;
226 : :
227 : 0 : }
228 : :
229 : :
230 : : // keyElement is created by parent QgsProjectPropertyKey
231 : 0 : bool QgsProjectPropertyValue::writeXml( QString const &nodeName,
232 : : QDomElement &keyElement,
233 : : QDomDocument &document )
234 : : {
235 : 0 : QDomElement valueElement = document.createElement( nodeName );
236 : :
237 : : // remember the type so that we can rebuild it when the project is read in
238 : 0 : valueElement.setAttribute( QStringLiteral( "type" ), mValue.typeName() );
239 : :
240 : :
241 : : // we handle string lists differently from other types in that we
242 : : // create a sequence of repeated elements to cover all the string list
243 : : // members; each value will be in a <value></value> tag.
244 : : // XXX Not the most elegant way to handle string lists?
245 : 0 : if ( QVariant::StringList == mValue.type() )
246 : : {
247 : 0 : QStringList sl = mValue.toStringList();
248 : :
249 : 0 : for ( QStringList::iterator i = sl.begin();
250 : 0 : i != sl.end();
251 : 0 : ++i )
252 : : {
253 : 0 : QDomElement stringListElement = document.createElement( QStringLiteral( "value" ) );
254 : 0 : QDomText valueText = document.createTextNode( *i );
255 : 0 : stringListElement.appendChild( valueText );
256 : :
257 : 0 : valueElement.appendChild( stringListElement );
258 : 0 : }
259 : 0 : }
260 : : else // we just plop the value in as plain ole text
261 : : {
262 : 0 : QDomText valueText = document.createTextNode( mValue.toString() );
263 : 0 : valueElement.appendChild( valueText );
264 : 0 : }
265 : :
266 : 0 : keyElement.appendChild( valueElement );
267 : :
268 : : return true;
269 : 0 : }
270 : :
271 : :
272 : 30 : QgsProjectPropertyKey::QgsProjectPropertyKey( const QString &name )
273 : 30 : : mName( name )
274 : 90 : {}
275 : :
276 : 41 : QgsProjectPropertyKey::~QgsProjectPropertyKey()
277 : 41 : {
278 : 22 : clearKeys();
279 : 41 : }
280 : :
281 : 0 : QVariant QgsProjectPropertyKey::value() const
282 : : {
283 : 0 : QgsProjectProperty *foundQgsProperty = mProperties.value( name() );
284 : :
285 : 0 : if ( !foundQgsProperty )
286 : : {
287 : 0 : QgsDebugMsg( QStringLiteral( "key has null child" ) );
288 : 0 : return QVariant(); // just return an QVariant::Invalid
289 : : }
290 : :
291 : 0 : return foundQgsProperty->value();
292 : 0 : }
293 : :
294 : :
295 : 0 : void QgsProjectPropertyKey::dump( int tabs ) const
296 : : {
297 : 0 : QString tabString;
298 : :
299 : 0 : tabString.fill( '\t', tabs );
300 : :
301 : 0 : QgsDebugMsgLevel( QStringLiteral( "%1name: %2" ).arg( tabString, name() ), 4 );
302 : :
303 : 0 : tabs++;
304 : 0 : tabString.fill( '\t', tabs );
305 : :
306 : 0 : if ( ! mProperties.isEmpty() )
307 : : {
308 : 0 : QHashIterator < QString, QgsProjectProperty * > i( mProperties );
309 : 0 : while ( i.hasNext() )
310 : : {
311 : 0 : if ( i.next().value()->isValue() )
312 : : {
313 : 0 : QgsProjectPropertyValue *propertyValue = static_cast<QgsProjectPropertyValue *>( i.value() );
314 : :
315 : 0 : if ( QVariant::StringList == propertyValue->value().type() )
316 : : {
317 : 0 : QgsDebugMsgLevel( QStringLiteral( "%1key: <%2> value:" ).arg( tabString, i.key() ), 4 );
318 : 0 : propertyValue->dump( tabs + 1 );
319 : 0 : }
320 : : else
321 : : {
322 : 0 : QgsDebugMsgLevel( QStringLiteral( "%1key: <%2> value: %3" ).arg( tabString, i.key(), propertyValue->value().toString() ), 4 );
323 : : }
324 : 0 : }
325 : : else
326 : : {
327 : 0 : QgsDebugMsgLevel( QStringLiteral( "%1key: <%2> subkey: <%3>" )
328 : : .arg( tabString,
329 : : i.key(),
330 : : static_cast<QgsProjectPropertyKey *>( i.value() )->name() ), 4 );
331 : 0 : i.value()->dump( tabs + 1 );
332 : : }
333 : :
334 : : #if 0
335 : : qDebug( "<%s>", name().toUtf8().constData() );
336 : : if ( i.value()->isValue() )
337 : : {
338 : : qDebug( " <%s>", i.key().toUtf8().constData() );
339 : : }
340 : : i.value()->dump();
341 : : if ( i.value()->isValue() )
342 : : {
343 : : qDebug( " </%s>", i.key().toUtf8().constData() );
344 : : }
345 : : qDebug( "</%s>", name().toUtf8().constData() );
346 : : #endif
347 : : }
348 : 0 : }
349 : :
350 : 0 : }
351 : :
352 : :
353 : :
354 : 0 : bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
355 : : {
356 : 0 : int i = 0;
357 : 0 : QDomNodeList subkeys = keyNode.childNodes();
358 : :
359 : 0 : while ( i < subkeys.count() )
360 : : {
361 : : // if the current node is an element that has a "type" attribute,
362 : : // then we know it's a leaf node; i.e., a subkey _value_, and not
363 : : // a subkey
364 : 0 : if ( subkeys.item( i ).hasAttributes() && // if we have attributes
365 : 0 : subkeys.item( i ).isElement() && // and we're an element
366 : 0 : subkeys.item( i ).toElement().hasAttribute( QStringLiteral( "type" ) ) ) // and we have a "type" attribute
367 : : {
368 : : // then we're a key value
369 : 0 : delete mProperties.take( subkeys.item( i ).nodeName() );
370 : 0 : mProperties.insert( subkeys.item( i ).nodeName(), new QgsProjectPropertyValue );
371 : :
372 : 0 : QDomNode subkey = subkeys.item( i );
373 : :
374 : 0 : if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
375 : : {
376 : 0 : QgsDebugMsg( QStringLiteral( "unable to parse key value %1" ).arg( subkeys.item( i ).nodeName() ) );
377 : 0 : }
378 : 0 : }
379 : : else // otherwise it's a subkey, so just recurse on down the remaining keys
380 : : {
381 : 0 : addKey( subkeys.item( i ).nodeName() );
382 : :
383 : 0 : QDomNode subkey = subkeys.item( i );
384 : :
385 : 0 : if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
386 : : {
387 : 0 : QgsDebugMsg( QStringLiteral( "unable to parse subkey %1" ).arg( subkeys.item( i ).nodeName() ) );
388 : 0 : }
389 : 0 : }
390 : :
391 : 0 : ++i;
392 : : }
393 : :
394 : : return true;
395 : 0 : }
396 : :
397 : :
398 : : /*
399 : : Property keys will always create a Dom element for itself and then
400 : : recursively call writeXml for any constituent properties.
401 : : */
402 : 0 : bool QgsProjectPropertyKey::writeXml( QString const &nodeName, QDomElement &element, QDomDocument &document )
403 : : {
404 : : // If it's an _empty_ node (i.e., one with no properties) we need to emit
405 : : // an empty place holder; else create new Dom elements as necessary.
406 : :
407 : 0 : QDomElement keyElement = document.createElement( nodeName ); // Dom element for this property key
408 : :
409 : 0 : if ( ! mProperties.isEmpty() )
410 : : {
411 : 0 : auto keys = mProperties.keys();
412 : 0 : std::sort( keys.begin(), keys.end() );
413 : :
414 : 0 : for ( const auto &key : std::as_const( keys ) )
415 : : {
416 : 0 : if ( !mProperties.value( key )->writeXml( key, keyElement, document ) )
417 : 0 : QgsMessageLog::logMessage( tr( "Failed to save project property %1" ).arg( key ) );
418 : : }
419 : 0 : }
420 : :
421 : 0 : element.appendChild( keyElement );
422 : :
423 : : return true;
424 : 0 : }
425 : :
426 : 0 : void QgsProjectPropertyKey::entryList( QStringList &entries ) const
427 : : {
428 : : // now add any leaf nodes to the entries list
429 : 0 : QHashIterator < QString, QgsProjectProperty * > i( mProperties );
430 : 0 : while ( i.hasNext() )
431 : : {
432 : : // add any of the nodes that have just a single value
433 : 0 : if ( i.next().value()->isLeaf() )
434 : : {
435 : 0 : entries.append( i.key() );
436 : 0 : }
437 : : }
438 : 0 : }
439 : :
440 : 0 : void QgsProjectPropertyKey::subkeyList( QStringList &entries ) const
441 : : {
442 : : // now add any leaf nodes to the entries list
443 : 0 : QHashIterator < QString, QgsProjectProperty * > i( mProperties );
444 : 0 : while ( i.hasNext() )
445 : : {
446 : : // add any of the nodes that have just a single value
447 : 0 : if ( !i.next().value()->isLeaf() )
448 : : {
449 : 0 : entries.append( i.key() );
450 : 0 : }
451 : : }
452 : 0 : }
453 : :
454 : :
455 : 0 : bool QgsProjectPropertyKey::isLeaf() const
456 : : {
457 : 0 : if ( 0 == count() )
458 : : {
459 : 0 : return true;
460 : : }
461 : 0 : else if ( 1 == count() )
462 : : {
463 : 0 : QHashIterator < QString, QgsProjectProperty * > i( mProperties );
464 : :
465 : 0 : if ( i.hasNext() && i.next().value()->isValue() )
466 : : {
467 : 0 : return true;
468 : : }
469 : 0 : }
470 : :
471 : 0 : return false;
472 : 0 : }
473 : :
474 : 5 : void QgsProjectPropertyKey::setName( const QString &name )
475 : : {
476 : 5 : mName = name;
477 : 5 : }
|