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