configurationparameters.cpp
1 /***************************************************************************
2  * Copyright (C) 2008-2009 by Tomassino Ferrauto *
3  * t_ferrauto@yahoo.it *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
20 
21 #include "realfactory.h"
22 #include "configurationnode.h"
23 #include "configurationparameters.h"
24 #include "parametersfileloadersaver.h"
25 #include "parametersettable.h"
26 #include "configurationexceptions.h"
27 #include <QFileInfo>
28 #include <QQueue>
29 #include <memory>
30 
31 namespace farsa {
32 
33 QMap<QString, ConfigurationParameters::FileFormat>& ConfigurationParameters::getFormatsMap()
34 {
35  static QMap<QString, ConfigurationParameters::FileFormat> formatsMap;
36 
37  return formatsMap;
38 }
39 
40 QMap<QString, QString>& ConfigurationParameters::getFileExtensionsMap()
41 {
42  static QMap<QString, QString> fileExtensionsMap;
43 
44  return fileExtensionsMap;
45 }
46 
47 bool ConfigurationParameters::registerFileFormat(QString format, ParametersFileLoaderSaver *fileLoaderSaver, QString defaultExtension)
48 {
49  // Checking whether the format already exists
50  QMap<QString, FileFormat>::iterator it = getFormatsMap().find(format);
51  if (it != getFormatsMap().end()) {
52  return false;
53  }
54 
55  // If defaultExtension is the empty string, changing it to format
56  if (defaultExtension.isEmpty()) {
57  defaultExtension = format;
58  }
59 
60  // Filling the structure with format informations
61  FileFormat f(format, fileLoaderSaver, defaultExtension);
62 
63  // Adding format to maps
64  getFormatsMap().insert(format, f);
65  getFileExtensionsMap().insert(defaultExtension, format);
66 
67  return true;
68 }
69 
71  m_factory(new RealFactory(*this)),
72  m_root(new ConfigurationNode(NULL, "", caseSensitive)),
73  m_getObjectFromGroupRecursionLevel(0),
74  m_objectsToConfigure(),
75  m_dontForgetGroupObjectAssociations(false),
76  m_resourcesUserPerRecursionLevel(QList<SimpleResourcesUser>() << SimpleResourcesUser())
77 {
78 }
79 
81  m_factory(new RealFactory(*this)),
82  m_root(new ConfigurationNode(NULL, "", true)),
83  m_getObjectFromGroupRecursionLevel(0),
84  m_objectsToConfigure(),
85  m_dontForgetGroupObjectAssociations(false),
86  m_resourcesUserPerRecursionLevel(QList<SimpleResourcesUser>() << SimpleResourcesUser())
87 {
88  // Copying the tree
89  copyTree(other.m_root.get());
90 }
91 
93 {
94  // Checking that we are not in a call to getObjectFromGroup
95  if (m_getObjectFromGroupRecursionLevel != 0) {
97  }
98 
99  // Checking for self-assignement
100  if (&other == this) {
101  return *this;
102  }
103 
104  // Copying the tree (this removes the previous one)
105  copyTree(other.m_root.get());
106 
107  // Now also substituting all observers
108  m_factory->clearObservers();
109  m_factory->addObservers(other.m_factory->getObservers());
110 
111  return *this;
112 }
113 
115 {
116  // Nothing to do here, we use auto_ptr
117 }
118 
120 {
121  return m_root->isCaseSensitive();
122 }
123 
125 {
126  m_root->clearAll();
127 }
128 
129 QStringList ConfigurationParameters::getGroupsList(QString group) const
130 {
131  return m_root->getNode(group)->getChildrenList();
132 }
133 
134 QStringList ConfigurationParameters::getGroupsWithPrefixList(QString group, QString prefix) const
135 {
136  // Building the regular expression matching the given prefix
137  QRegExp filter(QString("^") + QRegExp::escape(prefix), (isCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive));
138 
139  return getFilteredGroupsList(group, filter);
140 }
141 
142 QStringList ConfigurationParameters::getFilteredGroupsList(QString group, QRegExp filter) const
143 {
144  return m_root->getNode(group)->getFilteredChildrenList(filter);
145 }
146 
148 {
149  // Splitting path
150  QStringList splittedPath = groupPath.split(ConfigurationParameters::GroupSeparator(), QString::SkipEmptyParts, Qt::CaseSensitive);
151 
152  // Now creating all nodes
153  ConfigurationNode* lastNode = m_root.get();
154  for (QStringList::iterator it = splittedPath.begin(); it != splittedPath.end(); it++) {
155  lastNode = lastNode->addNode(*it);
156  }
157 }
158 
160 {
161  //--- extract the group name
162  QString groupName = groupPath.section( ConfigurationParameters::GroupSeparator(), -1 );
163  //--- extract the path
164  QString groupParent = groupPath.section( ConfigurationParameters::GroupSeparator(), 0, -2 );
165  return m_root->getNode(groupParent)->deleteNode(groupName);
166 }
167 
168 bool ConfigurationParameters::renameGroup(QString oldGroupPath, QString newGroupName)
169 {
170  //--- extract the group name
171  QString groupName = oldGroupPath.section( ConfigurationParameters::GroupSeparator(), -1 );
172  //--- extract the path
173  QString groupParent = oldGroupPath.section( ConfigurationParameters::GroupSeparator(), 0, -2 );
174  return m_root->getNode(groupParent)->renameNode(groupName, newGroupName);
175 }
176 
177 bool ConfigurationParameters::copyGroupTree(QString sourceGroup, QString destGroup) {
178  ConfigurationNode* sourceNode = m_root->getNode( sourceGroup );
179  if ( sourceNode->isNull() ) {
180  return false;
181  }
182  ConfigurationNode* destNode = m_root->getNode( destGroup );
183  if ( destNode->isNull() ) {
184  // create the destination node
185  createGroup( destGroup );
186  destNode = m_root->getNode( destGroup );
187  }
188  copyNode( sourceNode, destNode );
189  return true;
190 }
191 
192 void ConfigurationParameters::createParameter(QString groupPath, QString parameter)
193 {
194  ConfigurationNode* node = m_root->getNode(groupPath);
195  if ( !node->isNull() ) {
196  node->addParameter( parameter );
197  }
198 }
199 
200 void ConfigurationParameters::deleteParameter(QString groupPath, QString parameter) {
201  ConfigurationNode* node = m_root->getNode(groupPath);
202  if ( !node->isNull() ) {
203  node->deleteParameter( parameter );
204  }
205 }
206 
207 bool ConfigurationParameters::startObjectParameters(QString groupPath, QString typeName, ParameterSettable* object)
208 {
209  // Creating the group
210  createGroup(groupPath);
211 
212  // Then adding the "type" parameter
213  createParameter(groupPath, QString("type"), typeName);
214 
215  // Now setting the object corresponding to the given group to object
216  return m_root->setObjectForNode(groupPath, object);
217 }
218 
219 QString ConfigurationParameters::getValue(QString path, bool alsoMatchParents) const
220 {
221  return m_root->getValue(path, alsoMatchParents);
222 }
223 
224 bool ConfigurationParameters::setValue(QString path, QString value)
225 {
226  return m_root->setValue(path, value);
227 }
228 
230 {
231  return m_root->setValue(path, object);
232 }
233 
234 QStringList ConfigurationParameters::getParametersList(QString group) const
235 {
236  return m_root->getNode(group)->getParametersList();
237 }
238 
239 QStringList ConfigurationParameters::getParametersWithPrefixList(QString group, QString prefix) const
240 {
241  // Building the regular expression matching the given prefix
242  QRegExp filter(QString("^") + QRegExp::escape(prefix), (isCaseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive));
243 
244  return getFilteredParametersList(group, filter);
245 }
246 
247 QStringList ConfigurationParameters::getFilteredParametersList(QString group, QRegExp filter) const
248 {
249  return m_root->getNode(group)->getFilteredParametersList(filter);
250 }
251 
253 {
254  m_dontForgetGroupObjectAssociations = true;
255 }
256 
258 {
259  m_dontForgetGroupObjectAssociations = false;
260 }
261 
263 {
264  // Descending the tree and calling resetObject on all nodes
265  QQueue<ConfigurationNode *> nodeQueue;
266  nodeQueue.enqueue(m_root.get());
267 
268  // Now descending the tree
269  while (!nodeQueue.isEmpty()) {
270  // Getting a node and calling resetObject on it
271  ConfigurationNode *curNode = nodeQueue.dequeue();
272  curNode->resetObject();
273 
274  // Now adding all children of current node to the queue
275  #if QT_VERSION_CHECK > 0x450000
276  nodeQueue.append(curNode->getChildrenNodesList());
277  #else
278  foreach( ConfigurationNode* an, curNode->getChildrenNodesList() ) {
279  nodeQueue.append( an );
280  }
281  #endif
282  }
283 }
284 
286 {
287  // We perform the update in two steps: first of all we create a map from pointers to group paths;
288  // then we search the whole tree for object parameters and set them
289 
290  // First step: descending the tree and creating the map
291  QMap<ParameterSettable *, QString> objectsMap;
292  QQueue<ConfigurationNode *> nodeQueue;
293  nodeQueue.enqueue(m_root.get());
294 
295  // Now descending the tree
296  while (!nodeQueue.isEmpty()) {
297  // Getting a node and the object for that node
298  ConfigurationNode *curNode = nodeQueue.dequeue();
300 
301  if (object.object != NULL) {
302  // Adding to map
303  // --- when saving an object reference parameter, the first GroupSeparator() will
304  // be removed to avoid to save something like that:
305  // object = /groupPath/nameOfObject
306  // instead of
307  // object = groupPath/nameOfObject
308  QString fullname = curNode->getFullName();
309  if ( fullname.startsWith( GroupSeparator() ) ) {
310  fullname.remove( 0, 1 );
311  }
312  objectsMap[object.object] = fullname; //curNode->getFullName();
313  }
314 
315  // Now adding all children of current node to the queue
316  #if QT_VERSION_CHECK > 0x450000
317  nodeQueue.append(curNode->getChildrenNodesList());
318  #else
319  foreach( ConfigurationNode* an, curNode->getChildrenNodesList() ) {
320  nodeQueue.append( an );
321  }
322  #endif
323  }
324 
325  // We have the list of objects and their path, now searching all object parameters
326  // and setting them (we re-use nodeQueue to descend the tree, it is empty for sure)
327  nodeQueue.enqueue(m_root.get());
328 
329  // Descending the tree one more time
330  while (!nodeQueue.isEmpty()) {
331  // Getting a node
332  ConfigurationNode *curNode = nodeQueue.dequeue();
333 
334  // Obtaining the list of object parameters
335  QStringList params = curNode->getObjectParametersList();
336 
337  for (QStringList::iterator it = params.begin(); it != params.end(); it++) {
338  // Getting the object for the parameter
339  ParameterSettable *obj = curNode->getObject(*it).object;
340 
341  // If we have a pointer to obj in the map, setting the value for the parameter
342  if (objectsMap.contains(obj)) {
343  curNode->setValue(*it, objectsMap[obj]);
344  } else {
345  curNode->setValue(*it, QString(""));
346  }
347  }
348 
349  // Now adding all children of current node to the queue
350  #if QT_VERSION_CHECK > 0x450000
351  nodeQueue.append(curNode->getChildrenNodesList());
352  #else
353  foreach( ConfigurationNode* an, curNode->getChildrenNodesList() ) {
354  nodeQueue.append( an );
355  }
356  #endif
357  }
358 }
359 
360 bool ConfigurationParameters::loadParameters(QString filename, bool keepOld, QString format)
361 {
362  // If format is the empty string, guessing it from filename extension
363  if (format.isEmpty()) {
364  format = formatFromFilenameExtension(filename);
365 
366  if (format.isNull()) {
367  return false;
368  }
369  }
370 
371  // Searching the format on the map
372  QMap<QString, FileFormat>::iterator it = getFormatsMap().find(format);
373 
374  // If not found, returning false
375  if (it == getFormatsMap().end()) {
376  return false;
377  }
378 
379  return it->fileLoaderSaver->load( filename, *this, keepOld );
380 }
381 
382 bool ConfigurationParameters::saveParameters(QString filename, QString format, bool append)
383 {
384  // Before saving updating all object references
386 
387  // If format is the empty string, guessing it from filename extension
388  if (format.isEmpty()) {
389  format = formatFromFilenameExtension(filename);
390 
391  if (format.isNull()) {
392  return false;
393  }
394  }
395 
396  // Searching the format on the map
397  QMap<QString, FileFormat>::iterator it = getFormatsMap().find(format);
398 
399  // If not found, returning false
400  if (it == getFormatsMap().end()) {
401  return false;
402  }
403 
404  return it->fileLoaderSaver->save( filename, *this, append );
405 }
406 
408 {
409  // Searching the ResourceManager containing the given resource
410  for (int i = m_getObjectFromGroupRecursionLevel; i >= 0; i--) {
411  if (m_resourcesUserPerRecursionLevel[i].hasResource(resourceName)) {
412  return &(m_resourcesUserPerRecursionLevel[i]);
413  }
414  }
415 
416  return NULL;
417 }
418 
420 {
421  m_resourcesUserPerRecursionLevel.last().shareResourcesWith(resourcesUser);
422 }
423 
425 {
426  m_factory->addObserver(observer);
427 }
428 
430  m_factory->addObservers( params.m_factory->getObservers() );
431 }
432 
433 QString ConfigurationParameters::formatFromFilenameExtension(QString filename) const
434 {
435  QFileInfo info(filename);
436 
437  QMap<QString, QString>::const_iterator it = getFileExtensionsMap().find(info.suffix());
438  if (it == getFileExtensionsMap().end()) {
439  return QString();
440  } else {
441  return *it;
442  }
443 }
444 
445 void ConfigurationParameters::copyTree(const ConfigurationNode *root)
446 {
447  // Allocating root. Parent is not copied as we suppose root to be a root of a tree
448  m_root.reset(new ConfigurationNode(NULL, root->getName(), root->isCaseSensitive()));
449 
450  // Now recursively copying the tree
451  copyNode(root, m_root.get());
452 }
453 
454 void ConfigurationParameters::copyNode(const ConfigurationNode *source, ConfigurationNode *target)
455 {
456  // First copying all parameters
457  QStringList params = source->getParametersList();
458  for (QStringList::const_iterator it = params.begin(); it != params.end(); it++) {
459  target->addParameter(*it);
460  target->setValue(*it, source->getValue(*it));
461  }
462 
463  // We don't copy the object associated to the node
464 
465  // Now creating children in target and recursively filling them
466  QStringList children = source->getChildrenList();
467  for (QStringList::const_iterator it = children.begin(); it != children.end(); it++) {
468  target->addNode(*it);
469  copyNode(source->getNode(*it), target->getNode(*it));
470  }
471 }
472 
473 bool ConfigurationParameters::setObjectFromGroupStatusToCreating(QString group)
474 {
475  if (m_getObjectFromGroupRecursionLevel == 0) {
476  return true;
477  }
478  return m_root->setObjectForNode(group, NULL, CreatingObject);
479 }
480 
481 bool ConfigurationParameters::setObjectFromGroupStatusToCreatedNotConfigured(QString group, ParameterSettable *object)
482 {
483  if (m_getObjectFromGroupRecursionLevel == 0) {
484  return true;
485  }
486  return m_root->setObjectForNode(group, object, ObjectCreatedNotConfigured);
487 }
488 
489 bool ConfigurationParameters::setObjectFromGroupStatusToConfiguring(QString group)
490 {
491  if (m_getObjectFromGroupRecursionLevel == 0) {
492  return true;
493  }
494  return m_root->setObjectForNode(group, NULL, ConfiguringObject);
495 }
496 
497 bool ConfigurationParameters::setObjectFromGroupStatusToCreatingAndConfiguring(QString group)
498 {
499  if (m_getObjectFromGroupRecursionLevel == 0) {
500  return true;
501  }
502  return m_root->setObjectForNode(group, NULL, CreatingAndConfiguringObject);
503 }
504 
505 bool ConfigurationParameters::setObjectFromGroupStatusToCreatedAndConfigured(QString group, ParameterSettable *object)
506 {
507  if (m_getObjectFromGroupRecursionLevel == 0) {
508  return true;
509  }
510  return m_root->setObjectForNode(group, object, ObjectCreatedAndConfigured);
511 }
512 
513 } // end namespace farsa