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