QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsprojectproperty.cpp
Go to the documentation of this file.
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 
27 {
28 }
29 
30 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 }
53 
54 bool QgsProjectPropertyValue::readXml( const QDomNode &keyNode )
55 {
56  // this *should* be a Dom element node
57  QDomElement subkeyElement = keyNode.toElement();
58 
59  // get the type so that we can properly parse the key value
60  QString typeString = subkeyElement.attribute( QStringLiteral( "type" ) );
61 
62  if ( typeString.isNull() )
63  {
64  QgsDebugMsg( QStringLiteral( "null ``type'' attribute for %1" ).arg( keyNode.nodeName() ) );
65 
66  return false;
67  }
68 
69  // the values come in as strings; we need to restore them to their
70  // original values *and* types
71  mValue.clear();
72 
73  // get the type associated with the value first
74  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  switch ( type )
84  {
85  case QVariant::Invalid:
86  QgsDebugMsg( QStringLiteral( "invalid value type %1 .. " ).arg( typeString ) );
87  return false;
88 
89  case QVariant::Map:
90  QgsDebugMsg( QStringLiteral( "no support for QVariant::Map" ) );
91  return false;
92 
93  case QVariant::List:
94  QgsDebugMsg( QStringLiteral( "no support for QVariant::List" ) );
95  return false;
96 
97  case QVariant::String:
98  mValue = subkeyElement.text(); // no translating necessary
99  break;
100 
101  case QVariant::StringList:
102  {
103  int i = 0;
104  QDomNodeList values = keyNode.childNodes();
105 
106  // all the QStringList values will be inside <value> elements
107  QStringList valueStringList;
108 
109  while ( i < values.count() )
110  {
111  if ( "value" == values.item( i ).nodeName() )
112  {
113  // <value>s have only one element, which contains actual string value
114  valueStringList.append( values.item( i ).firstChild().nodeValue() );
115  }
116  else
117  {
118  QgsDebugMsg( QStringLiteral( "non <value> element ``%1'' in string list" ).arg( values.item( i ).nodeName() ) );
119  }
120 
121  ++i;
122  }
123 
124  mValue = valueStringList;
125  break;
126  }
127 
128  case QVariant::Font:
129  QgsDebugMsg( QStringLiteral( "no support for QVariant::Font" ) );
130  return false;
131 
132  case QVariant::Pixmap:
133  QgsDebugMsg( QStringLiteral( "no support for QVariant::Pixmap" ) );
134  return false;
135 
136  case QVariant::Brush:
137  QgsDebugMsg( QStringLiteral( "no support for QVariant::Brush" ) );
138  return false;
139 
140  case QVariant::Rect:
141  QgsDebugMsg( QStringLiteral( "no support for QVariant::Rect" ) );
142  return false;
143 
144  case QVariant::Size:
145  QgsDebugMsg( QStringLiteral( "no support for QVariant::Size" ) );
146  return false;
147 
148  case QVariant::Color:
149  QgsDebugMsg( QStringLiteral( "no support for QVariant::Color" ) );
150  return false;
151 
152  case QVariant::Palette:
153  QgsDebugMsg( QStringLiteral( "no support for QVariant::Palette" ) );
154  return false;
155 
156  case QVariant::Point:
157  QgsDebugMsg( QStringLiteral( "no support for QVariant::Point" ) );
158  return false;
159 
160  case QVariant::Image:
161  QgsDebugMsg( QStringLiteral( "no support for QVariant::Image" ) );
162  return false;
163 
164  case QVariant::Int:
165  mValue = QVariant( subkeyElement.text() ).toInt();
166  break;
167 
168  case QVariant::UInt:
169  mValue = QVariant( subkeyElement.text() ).toUInt();
170  break;
171 
172  case QVariant::Bool:
173  mValue = QVariant( subkeyElement.text() ).toBool();
174  break;
175 
176  case QVariant::Double:
177  mValue = QVariant( subkeyElement.text() ).toDouble();
178  break;
179 
180  case QVariant::ByteArray:
181  mValue = QVariant( subkeyElement.text() ).toByteArray();
182  break;
183 
184  case QVariant::Polygon:
185  QgsDebugMsg( QStringLiteral( "no support for QVariant::Polygon" ) );
186  return false;
187 
188  case QVariant::Region:
189  QgsDebugMsg( QStringLiteral( "no support for QVariant::Region" ) );
190  return false;
191 
192  case QVariant::Bitmap:
193  QgsDebugMsg( QStringLiteral( "no support for QVariant::Bitmap" ) );
194  return false;
195 
196  case QVariant::Cursor:
197  QgsDebugMsg( QStringLiteral( "no support for QVariant::Cursor" ) );
198  return false;
199 
200  case QVariant::BitArray :
201  QgsDebugMsg( QStringLiteral( "no support for QVariant::BitArray" ) );
202  return false;
203 
204  case QVariant::KeySequence :
205  QgsDebugMsg( QStringLiteral( "no support for QVariant::KeySequence" ) );
206  return false;
207 
208  case QVariant::Pen :
209  QgsDebugMsg( QStringLiteral( "no support for QVariant::Pen" ) );
210  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  QgsDebugMsg( QStringLiteral( "unsupported value type %1 .. not properly translated to QVariant" ).arg( typeString ) );
223  }
224 
225  return true;
226 
227 }
228 
229 
230 // keyElement is created by parent QgsProjectPropertyKey
231 bool QgsProjectPropertyValue::writeXml( QString const &nodeName,
232  QDomElement &keyElement,
233  QDomDocument &document )
234 {
235  QDomElement valueElement = document.createElement( nodeName );
236 
237  // remember the type so that we can rebuild it when the project is read in
238  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  if ( QVariant::StringList == mValue.type() )
246  {
247  QStringList sl = mValue.toStringList();
248 
249  for ( QStringList::iterator i = sl.begin();
250  i != sl.end();
251  ++i )
252  {
253  QDomElement stringListElement = document.createElement( QStringLiteral( "value" ) );
254  QDomText valueText = document.createTextNode( *i );
255  stringListElement.appendChild( valueText );
256 
257  valueElement.appendChild( stringListElement );
258  }
259  }
260  else // we just plop the value in as plain ole text
261  {
262  QDomText valueText = document.createTextNode( mValue.toString() );
263  valueElement.appendChild( valueText );
264  }
265 
266  keyElement.appendChild( valueElement );
267 
268  return true;
269 }
270 
271 
273  : mName( name )
274 {}
275 
277 {
278  clearKeys();
279 }
280 
282 {
283  QgsProjectProperty *foundQgsProperty = mProperties.value( name() );
284 
285  if ( !foundQgsProperty )
286  {
287  QgsDebugMsg( QStringLiteral( "key has null child" ) );
288  return QVariant(); // just return an QVariant::Invalid
289  }
290 
291  return foundQgsProperty->value();
292 }
293 
294 
295 void QgsProjectPropertyKey::dump( int tabs ) const
296 {
297  QString tabString;
298 
299  tabString.fill( '\t', tabs );
300 
301  QgsDebugMsgLevel( QStringLiteral( "%1name: %2" ).arg( tabString, name() ), 4 );
302 
303  tabs++;
304  tabString.fill( '\t', tabs );
305 
306  if ( ! mProperties.isEmpty() )
307  {
308  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
309  while ( i.hasNext() )
310  {
311  if ( i.next().value()->isValue() )
312  {
313  QgsProjectPropertyValue *propertyValue = static_cast<QgsProjectPropertyValue *>( i.value() );
314 
315  if ( QVariant::StringList == propertyValue->value().type() )
316  {
317  QgsDebugMsgLevel( QStringLiteral( "%1key: <%2> value:" ).arg( tabString, i.key() ), 4 );
318  propertyValue->dump( tabs + 1 );
319  }
320  else
321  {
322  QgsDebugMsgLevel( QStringLiteral( "%1key: <%2> value: %3" ).arg( tabString, i.key(), propertyValue->value().toString() ), 4 );
323  }
324  }
325  else
326  {
327  QgsDebugMsgLevel( QStringLiteral( "%1key: <%2> subkey: <%3>" )
328  .arg( tabString,
329  i.key(),
330  static_cast<QgsProjectPropertyKey *>( i.value() )->name() ), 4 );
331  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  }
349 
350 }
351 
352 
353 
354 bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
355 {
356  int i = 0;
357  QDomNodeList subkeys = keyNode.childNodes();
358 
359  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  if ( subkeys.item( i ).hasAttributes() && // if we have attributes
365  subkeys.item( i ).isElement() && // and we're an element
366  subkeys.item( i ).toElement().hasAttribute( QStringLiteral( "type" ) ) ) // and we have a "type" attribute
367  {
368  // then we're a key value
369  delete mProperties.take( subkeys.item( i ).nodeName() );
370  mProperties.insert( subkeys.item( i ).nodeName(), new QgsProjectPropertyValue );
371 
372  QDomNode subkey = subkeys.item( i );
373 
374  if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
375  {
376  QgsDebugMsg( QStringLiteral( "unable to parse key value %1" ).arg( subkeys.item( i ).nodeName() ) );
377  }
378  }
379  else // otherwise it's a subkey, so just recurse on down the remaining keys
380  {
381  addKey( subkeys.item( i ).nodeName() );
382 
383  QDomNode subkey = subkeys.item( i );
384 
385  if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
386  {
387  QgsDebugMsg( QStringLiteral( "unable to parse subkey %1" ).arg( subkeys.item( i ).nodeName() ) );
388  }
389  }
390 
391  ++i;
392  }
393 
394  return true;
395 }
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 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  QDomElement keyElement = document.createElement( nodeName ); // Dom element for this property key
408 
409  if ( ! mProperties.isEmpty() )
410  {
411  auto keys = mProperties.keys();
412  std::sort( keys.begin(), keys.end() );
413 
414  for ( const auto &key : std::as_const( keys ) )
415  {
416  if ( !mProperties.value( key )->writeXml( key, keyElement, document ) )
417  QgsMessageLog::logMessage( tr( "Failed to save project property %1" ).arg( key ) );
418  }
419  }
420 
421  element.appendChild( keyElement );
422 
423  return true;
424 }
425 
426 void QgsProjectPropertyKey::entryList( QStringList &entries ) const
427 {
428  // now add any leaf nodes to the entries list
429  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
430  while ( i.hasNext() )
431  {
432  // add any of the nodes that have just a single value
433  if ( i.next().value()->isLeaf() )
434  {
435  entries.append( i.key() );
436  }
437  }
438 }
439 
440 void QgsProjectPropertyKey::subkeyList( QStringList &entries ) const
441 {
442  // now add any leaf nodes to the entries list
443  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
444  while ( i.hasNext() )
445  {
446  // add any of the nodes that have just a single value
447  if ( !i.next().value()->isLeaf() )
448  {
449  entries.append( i.key() );
450  }
451  }
452 }
453 
454 
456 {
457  if ( 0 == count() )
458  {
459  return true;
460  }
461  else if ( 1 == count() )
462  {
463  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
464 
465  if ( i.hasNext() && i.next().value()->isValue() )
466  {
467  return true;
468  }
469  }
470 
471  return false;
472 }
473 
474 void QgsProjectPropertyKey::setName( const QString &name )
475 {
476  mName = name;
477 }
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Project property key node.
bool isLeaf() const override
Returns true if property is a leaf node.
QString name() const
The name of the property is used as identifier.
QgsProjectPropertyKey(const QString &name=QString())
Create a new QgsProjectPropertyKey with the specified identifier.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
void dump(int tabs=0) const override
Dumps out the keys and values.
virtual void clearKeys()
Deletes any sub-nodes from the property.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
void setName(const QString &name)
The name of the property is used as identifier.
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
int count() const
Returns the number of sub-keys contained by this property.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
Project property value node, contains a QgsProjectPropertyKey's value.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
QVariant value() const override
Returns the node's value.
void dump(int tabs=0) const override
Dumps out the keys and values.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
An Abstract Base Class for QGIS project property hierarchys.
virtual QVariant value() const =0
Returns the node's value.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38