QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  QgsDebugMsg( QStringLiteral( "%1[%2] " ).arg( tabString, string ) );
45  }
46  }
47  else
48  {
49  QgsDebugMsg( QStringLiteral( "%1%2" ).arg( tabString, mValue.toString() ) );
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 
222  default :
223  QgsDebugMsg( QStringLiteral( "unsupported value type %1 .. not propertly translated to QVariant" ).arg( typeString ) );
224  }
225 
226  return true;
227 
228 }
229 
230 
231 // keyElement is created by parent QgsProjectPropertyKey
232 bool QgsProjectPropertyValue::writeXml( QString const &nodeName,
233  QDomElement &keyElement,
234  QDomDocument &document )
235 {
236  QDomElement valueElement = document.createElement( nodeName );
237 
238  // remember the type so that we can rebuild it when the project is read in
239  valueElement.setAttribute( QStringLiteral( "type" ), mValue.typeName() );
240 
241 
242  // we handle string lists differently from other types in that we
243  // create a sequence of repeated elements to cover all the string list
244  // members; each value will be in a <value></value> tag.
245  // XXX Not the most elegant way to handle string lists?
246  if ( QVariant::StringList == mValue.type() )
247  {
248  QStringList sl = mValue.toStringList();
249 
250  for ( QStringList::iterator i = sl.begin();
251  i != sl.end();
252  ++i )
253  {
254  QDomElement stringListElement = document.createElement( QStringLiteral( "value" ) );
255  QDomText valueText = document.createTextNode( *i );
256  stringListElement.appendChild( valueText );
257 
258  valueElement.appendChild( stringListElement );
259  }
260  }
261  else // we just plop the value in as plain ole text
262  {
263  QDomText valueText = document.createTextNode( mValue.toString() );
264  valueElement.appendChild( valueText );
265  }
266 
267  keyElement.appendChild( valueElement );
268 
269  return true;
270 }
271 
272 
274  : mName( name )
275 {}
276 
278 {
279  clearKeys();
280 }
281 
283 {
284  QgsProjectProperty *foundQgsProperty = mProperties.value( name() );
285 
286  if ( !foundQgsProperty )
287  {
288  QgsDebugMsg( QStringLiteral( "key has null child" ) );
289  return QVariant(); // just return an QVariant::Invalid
290  }
291 
292  return foundQgsProperty->value();
293 }
294 
295 
296 void QgsProjectPropertyKey::dump( int tabs ) const
297 {
298  QString tabString;
299 
300  tabString.fill( '\t', tabs );
301 
302  QgsDebugMsg( QStringLiteral( "%1name: %2" ).arg( tabString, name() ) );
303 
304  tabs++;
305  tabString.fill( '\t', tabs );
306 
307  if ( ! mProperties.isEmpty() )
308  {
309  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
310  while ( i.hasNext() )
311  {
312  if ( i.next().value()->isValue() )
313  {
314  QgsProjectPropertyValue *propertyValue = static_cast<QgsProjectPropertyValue *>( i.value() );
315 
316  if ( QVariant::StringList == propertyValue->value().type() )
317  {
318  QgsDebugMsg( QStringLiteral( "%1key: <%2> value:" ).arg( tabString, i.key() ) );
319  propertyValue->dump( tabs + 1 );
320  }
321  else
322  {
323  QgsDebugMsg( QStringLiteral( "%1key: <%2> value: %3" ).arg( tabString, i.key(), propertyValue->value().toString() ) );
324  }
325  }
326  else
327  {
328  QgsDebugMsg( QStringLiteral( "%1key: <%2> subkey: <%3>" )
329  .arg( tabString,
330  i.key(),
331  static_cast<QgsProjectPropertyKey *>( i.value() )->name() ) );
332  i.value()->dump( tabs + 1 );
333  }
334 
335 #if 0
336  qDebug( "<%s>", name().toUtf8().constData() );
337  if ( i.value()->isValue() )
338  {
339  qDebug( " <%s>", i.key().toUtf8().constData() );
340  }
341  i.value()->dump();
342  if ( i.value()->isValue() )
343  {
344  qDebug( " </%s>", i.key().toUtf8().constData() );
345  }
346  qDebug( "</%s>", name().toUtf8().constData() );
347 #endif
348  }
349  }
350 
351 }
352 
353 
354 
355 bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
356 {
357  int i = 0;
358  QDomNodeList subkeys = keyNode.childNodes();
359 
360  while ( i < subkeys.count() )
361  {
362  // if the current node is an element that has a "type" attribute,
363  // then we know it's a leaf node; i.e., a subkey _value_, and not
364  // a subkey
365  if ( subkeys.item( i ).hasAttributes() && // if we have attributes
366  subkeys.item( i ).isElement() && // and we're an element
367  subkeys.item( i ).toElement().hasAttribute( QStringLiteral( "type" ) ) ) // and we have a "type" attribute
368  {
369  // then we're a key value
370  delete mProperties.take( subkeys.item( i ).nodeName() );
371  mProperties.insert( subkeys.item( i ).nodeName(), new QgsProjectPropertyValue );
372 
373  QDomNode subkey = subkeys.item( i );
374 
375  if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
376  {
377  QgsDebugMsg( QStringLiteral( "unable to parse key value %1" ).arg( subkeys.item( i ).nodeName() ) );
378  }
379  }
380  else // otherwise it's a subkey, so just recurse on down the remaining keys
381  {
382  addKey( subkeys.item( i ).nodeName() );
383 
384  QDomNode subkey = subkeys.item( i );
385 
386  if ( !mProperties[subkeys.item( i ).nodeName()]->readXml( subkey ) )
387  {
388  QgsDebugMsg( QStringLiteral( "unable to parse subkey %1" ).arg( subkeys.item( i ).nodeName() ) );
389  }
390  }
391 
392  ++i;
393  }
394 
395  return true;
396 }
397 
398 
399 /*
400  Property keys will always create a Dom element for itself and then
401  recursively call writeXml for any constituent properties.
402 */
403 bool QgsProjectPropertyKey::writeXml( QString const &nodeName, QDomElement &element, QDomDocument &document )
404 {
405  // If it's an _empty_ node (i.e., one with no properties) we need to emit
406  // an empty place holder; else create new Dom elements as necessary.
407 
408  QDomElement keyElement = document.createElement( nodeName ); // Dom element for this property key
409 
410  if ( ! mProperties.isEmpty() )
411  {
412  auto keys = mProperties.keys();
413  std::sort( keys.begin(), keys.end() );
414 
415  for ( const auto &key : qgis::as_const( keys ) )
416  {
417  if ( !mProperties.value( key )->writeXml( key, keyElement, document ) )
418  QgsMessageLog::logMessage( tr( "Failed to save project property %1" ).arg( key ) );
419  }
420  }
421 
422  element.appendChild( keyElement );
423 
424  return true;
425 }
426 
427 void QgsProjectPropertyKey::entryList( QStringList &entries ) const
428 {
429  // now add any leaf nodes to the entries list
430  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
431  while ( i.hasNext() )
432  {
433  // add any of the nodes that have just a single value
434  if ( i.next().value()->isLeaf() )
435  {
436  entries.append( i.key() );
437  }
438  }
439 }
440 
441 void QgsProjectPropertyKey::subkeyList( QStringList &entries ) const
442 {
443  // now add any leaf nodes to the entries list
444  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
445  while ( i.hasNext() )
446  {
447  // add any of the nodes that have just a single value
448  if ( !i.next().value()->isLeaf() )
449  {
450  entries.append( i.key() );
451  }
452  }
453 }
454 
455 
457 {
458  if ( 0 == count() )
459  {
460  return true;
461  }
462  else if ( 1 == count() )
463  {
464  QHashIterator < QString, QgsProjectProperty * > i( mProperties );
465 
466  if ( i.hasNext() && i.next().value()->isValue() )
467  {
468  return true;
469  }
470  }
471 
472  return false;
473 }
474 
475 void QgsProjectPropertyKey::setName( const QString &name )
476 {
477  mName = name;
478 }
QgsProjectPropertyKey(const QString &name=QString())
Create a new QgsProjectPropertyKey with the specified identifier.
QVariant value() const override
Returns the node&#39;s value.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
virtual QVariant value() const =0
Returns the node&#39;s value.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
Project property value node, contains a QgsProjectPropertyKey&#39;s value.
void dump(int tabs=0) const override
Dumps out the keys and values.
QString name() const
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.
int count() const
Returns the number of sub-keys contained by this property.
virtual void clearKeys()
Deletes any sub-nodes from the property.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
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.
Project property key node.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
An Abstract Base Class for QGIS project property hierarchys.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
bool isLeaf() const override
Returns true if property is a leaf node.
void setName(const QString &name)
The name of the property is used as identifier.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.