QGIS API Documentation  2.11.0-Master
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 
24 void QgsPropertyValue::dump( int tabs ) const
25 {
26  QString tabString;
27  tabString.fill( '\t', tabs );
28 
29  if ( QVariant::StringList == value_.type() )
30  {
31  QStringList sl = value_.toStringList();
32 
33  for ( QStringList::const_iterator i = sl.begin(); i != sl.end(); ++i )
34  {
35  QgsDebugMsg( QString( "%1[%2] " ).arg( tabString ).arg( *i ) );
36  }
37  }
38  else
39  {
40  QgsDebugMsg( QString( "%1%2" ).arg( tabString ).arg( value_.toString() ) );
41  }
42 } // QgsPropertyValue::dump()
43 
44 
46 {
47  // this *should* be a Dom element node
48  QDomElement subkeyElement = keyNode.toElement();
49 
50  // get the type so that we can properly parse the key value
51  QString typeString = subkeyElement.attribute( "type" );
52 
53  if ( QString::null == typeString )
54  {
55  QgsDebugMsg( QString( "null ``type'' attribute for %1" ).arg( keyNode.nodeName() ) );
56 
57  return false;
58  }
59 
60  // the values come in as strings; we need to restore them to their
61  // original values *and* types
62  value_.clear();
63 
64  // get the type associated with the value first
65  QVariant::Type type = QVariant::nameToType( typeString.toLocal8Bit().constData() );
66 
67  // This huge switch is left-over from an earlier incarnation of
68  // QgsProject where there was a fine level of granularity for value
69  // types. The current interface, borrowed from QSettings, supports a
70  // very small sub-set of these types. However, I've left all the
71  // other types just in case the interface is expanded to include these
72  // other types.
73 
74  switch ( type )
75  {
76  case QVariant::Invalid:
77  QgsDebugMsg( QString( "invalid value type %1 .. " ).arg( typeString ) );
78  return false;
79 
80  case QVariant::Map:
81  QgsDebugMsg( "no support for QVariant::Map" );
82  return false;
83 
84  case QVariant::List:
85  QgsDebugMsg( "no support for QVariant::List" );
86  return false;
87 
88  case QVariant::String:
89  value_ = subkeyElement.text(); // no translating necessary
90  break;
91 
92  case QVariant::StringList:
93  {
94  int i = 0;
95  QDomNodeList values = keyNode.childNodes();
96 
97  // all the QStringList values will be inside <value> elements
98  QStringList valueStringList;
99 
100  while ( i < values.count() )
101  {
102  if ( "value" == values.item( i ).nodeName() )
103  { // <value>s have only one element, which contains actual string value
104  valueStringList.append( values.item( i ).firstChild().nodeValue() );
105  }
106  else
107  {
108  QgsDebugMsg( QString( "non <value> element ``%1'' in string list" ).arg( values.item( i ).nodeName() ) );
109  }
110 
111  ++i;
112  }
113 
114  value_ = valueStringList;
115  break;
116  }
117 
118  case QVariant::Font:
119  QgsDebugMsg( "no support for QVariant::Font" );
120  return false;
121 
122  case QVariant::Pixmap:
123  QgsDebugMsg( "no support for QVariant::Pixmap" );
124  return false;
125 
126  case QVariant::Brush:
127  QgsDebugMsg( "no support for QVariant::Brush" );
128  return false;
129 
130  case QVariant::Rect:
131  QgsDebugMsg( "no support for QVariant::Rect" );
132  return false;
133 
134  case QVariant::Size:
135  QgsDebugMsg( "no support for QVariant::Size" );
136  return false;
137 
138  case QVariant::Color:
139  QgsDebugMsg( "no support for QVariant::Color" );
140  return false;
141 
142  case QVariant::Palette:
143  QgsDebugMsg( "no support for QVariant::Palette" );
144  return false;
145 
146  case QVariant::Point:
147  QgsDebugMsg( "no support for QVariant::Point" );
148  return false;
149 
150  case QVariant::Image:
151  QgsDebugMsg( "no support for QVariant::Image" );
152  return false;
153 
154  case QVariant::Int:
155  value_ = QVariant( subkeyElement.text() ).toInt();
156  break;
157 
158  case QVariant::UInt:
159  value_ = QVariant( subkeyElement.text() ).toUInt();
160  break;
161 
162  case QVariant::Bool:
163  value_ = QVariant( subkeyElement.text() ).toBool();
164  break;
165 
166  case QVariant::Double:
167  value_ = QVariant( subkeyElement.text() ).toDouble();
168  break;
169 
170  case QVariant::ByteArray:
171  value_ = QVariant( subkeyElement.text() ).toByteArray();
172  break;
173 
174  case QVariant::Polygon:
175  QgsDebugMsg( "no support for QVariant::Polygon" );
176  return false;
177 
178  case QVariant::Region:
179  QgsDebugMsg( "no support for QVariant::Region" );
180  return false;
181 
182  case QVariant::Bitmap:
183  QgsDebugMsg( "no support for QVariant::Bitmap" );
184  return false;
185 
186  case QVariant::Cursor:
187  QgsDebugMsg( "no support for QVariant::Cursor" );
188  return false;
189 
190  case QVariant::BitArray :
191  QgsDebugMsg( "no support for QVariant::BitArray" );
192  return false;
193 
194  case QVariant::KeySequence :
195  QgsDebugMsg( "no support for QVariant::KeySequence" );
196  return false;
197 
198  case QVariant::Pen :
199  QgsDebugMsg( "no support for QVariant::Pen" );
200  return false;
201 
202  //
203  // QGIS DIES NOT SUPPORT THESE VARIANT TYPES IN VERSION 3.1 DISABLING FOR NOW
204  //
205  /*
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  */
214  default :
215  QgsDebugMsg( QString( "unsupported value type %1 .. not propertly translated to QVariant" ).arg( typeString ) );
216  }
217 
218  return true;
219 
220 } // QgsPropertyValue::readXML
221 
222 
226 bool QgsPropertyValue::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( "type", value_.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 == value_.type() )
241  {
242  QStringList sl = value_.toStringList();
243 
244  for ( QStringList::iterator i = sl.begin();
245  i != sl.end();
246  ++i )
247  {
248  QDomElement stringListElement = document.createElement( "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( value_.toString() );
258  valueElement.appendChild( valueText );
259  }
260 
261  keyElement.appendChild( valueElement );
262 
263  return true;
264 } // QgsPropertyValue::writeXML
265 
266 
268  : mName( name )
269 {}
270 
272 {
273  clearKeys();
274 }
275 
277 {
278  QgsProperty *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 } // QVariant QgsPropertyKey::value()
288 
289 
290 void QgsPropertyKey::dump( int tabs ) const
291 {
292  QString tabString;
293 
294  tabString.fill( '\t', tabs );
295 
296  QgsDebugMsg( QString( "%1name: %2" ).arg( tabString ).arg( name() ) );
297 
298  tabs++;
299  tabString.fill( '\t', tabs );
300 
301  if ( ! mProperties.isEmpty() )
302  {
304  while ( i.hasNext() )
305  {
306  if ( i.next().value()->isValue() )
307  {
308  QgsPropertyValue * propertyValue = static_cast<QgsPropertyValue*>( i.value() );
309 
310  if ( QVariant::StringList == propertyValue->value().type() )
311  {
312  QgsDebugMsg( QString( "%1key: <%2> value:" ).arg( tabString ).arg( i.key() ) );
313  propertyValue->dump( tabs + 1 );
314  }
315  else
316  {
317  QgsDebugMsg( QString( "%1key: <%2> value: %3" ).arg( tabString ).arg( i.key() ).arg( propertyValue->value().toString() ) );
318  }
319  }
320  else
321  {
322  QgsDebugMsg( QString( "%1key: <%2> subkey: <%3>" )
323  .arg( tabString )
324  .arg( i.key() )
325  .arg( dynamic_cast<QgsPropertyKey*>( 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 } // QgsPropertyKey::dump
346 
347 
348 
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( "type" ) ) // and we have a "type" attribute
362  { // then we're a key value
363  delete mProperties.take( subkeys.item( i ).nodeName() );
364  mProperties.insert( subkeys.item( i ).nodeName(), new QgsPropertyValue );
365 
366  QDomNode subkey = subkeys.item( i );
367 
368  if ( !mProperties[subkeys.item( i ).nodeName()]->readXML( subkey ) )
369  {
370  QgsDebugMsg( QString( "unable to parse key value %1" ).arg( subkeys.item( i ).nodeName() ) );
371  }
372  }
373  else // otherwise it's a subkey, so just
374  // 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 } // QgsPropertyKey::readXML(QDomNode & keyNode)
391 
392 
397 bool QgsPropertyKey::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  {
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 } // QgsPropertyKey::writeXML
421 
422 
423 
426 void QgsPropertyKey::entryList( QStringList & entries ) const
427 {
428  // now add any leaf nodes to the entries list
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 } // QgsPropertyKey::entryList
439 
440 
441 
443 {
444  // now add any leaf nodes to the entries list
446  while ( i.hasNext() )
447  {
448  // add any of the nodes that have just a single value
449  if ( !i.next().value()->isLeaf() )
450  {
451  entries.append( i.key() );
452  }
453  }
454 } // QgsPropertyKey::subkeyList
455 
456 
458 {
459  if ( 0 == count() )
460  {
461  return true;
462  }
463  else if ( 1 == count() )
464  {
466 
467  if ( i.hasNext() && i.next().value()->isValue() )
468  {
469  return true;
470  }
471  }
472 
473  return false;
474 } // 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
void entryList(QStringList &entries) const
return keys that do not contain other keys
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
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
virtual QVariant value() const =0
return the node'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
QVariant value() const override
return the node's value
QDomText createTextNode(const QString &value)
iterator end()
const T value(const Key &key) const
QByteArray toLocal8Bit() const
void subkeyList(QStringList &entries) const
return keys that contain other keys
void clear()
QDomNode firstChild() const
QStringList toStringList() const
T take(const Key &key)
QgsPropertyKey(const QString &name="")
bool isEmpty() const
const char * typeName() const
bool readXML(QDomNode &keyNode) override
restores property hierarchy to given Dom node
bool hasAttributes() const
const QString & name() const
every key has a name
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
int count() const
how many elements are contained within this one?