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