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