source: finroc_plugins_parameters/tConfigFile.cpp @ 78:df42a6d9c51d

17.03
Last change on this file since 78:df42a6d9c51d was 78:df42a6d9c51d, checked in by Max Reichardt <mreichardt@…>, 2 years ago

Merged with 14.08 from Robot Makers

File size: 13.8 KB
Line 
1//
2// You received this file as part of Finroc
3// A framework for intelligent robot control
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    plugins/parameters/tConfigFile.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-11-28
27 *
28 */
29//----------------------------------------------------------------------
30#include "plugins/parameters/tConfigFile.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include "rrlib/rtti/rtti.h"
36#include "core/file_lookup.h"
37
38//----------------------------------------------------------------------
39// Internal includes with ""
40//----------------------------------------------------------------------
41#include "plugins/parameters/internal/tParameterInfo.h"
42
43//----------------------------------------------------------------------
44// Debugging
45//----------------------------------------------------------------------
46#include <cassert>
47
48//----------------------------------------------------------------------
49// Namespace usage
50//----------------------------------------------------------------------
51
52//----------------------------------------------------------------------
53// Namespace declaration
54//----------------------------------------------------------------------
55namespace finroc
56{
57namespace parameters
58{
59
60//----------------------------------------------------------------------
61// Forward declarations / typedefs / enums
62//----------------------------------------------------------------------
63#ifdef _LIB_RRLIB_XML_PRESENT_
64typedef rrlib::xml::tNode tXMLNode;
65
66//----------------------------------------------------------------------
67// Const values
68//----------------------------------------------------------------------
69/*! Separator entries are divided with */
70static const std::string cSEPARATOR("/");
71
72/*! Branch name in XML */
73static const std::string cXML_BRANCH_NAME("node");
74
75/*! Leaf name in XML */
76static const std::string cXML_LEAF_NAME("value");
77#endif
78
79/*! Initializes annotation type so that it can be transferred to finstruct */
80static rrlib::rtti::tDataType<tConfigFile> cTYPE;
81
82//----------------------------------------------------------------------
83// Implementation
84//----------------------------------------------------------------------
85
86tConfigFile::tConfigFile() :
87#ifdef _LIB_RRLIB_XML_PRESENT_
88  wrapped(),
89#endif
90  filename(),
91  active(true)
92{
93#ifdef _LIB_RRLIB_XML_PRESENT_
94  wrapped.AddRootNode(cXML_BRANCH_NAME);
95#endif
96}
97
98tConfigFile::tConfigFile(const std::string& filename, bool optional) :
99#ifdef _LIB_RRLIB_XML_PRESENT_
100  wrapped(),
101#endif
102  filename(filename),
103  active(true)
104{
105#ifdef _LIB_RRLIB_XML_PRESENT_
106  if (core::FinrocFileExists(filename))
107  {
108    try
109    {
110      wrapped = core::GetFinrocXMLDocument(filename, false); // false = do not validate with dtd
111      return;
112    }
113    catch (const std::exception& e)
114    {
115      FINROC_LOG_PRINT(ERROR, e);
116    }
117  }
118  else if (!optional)
119  {
120    FINROC_LOG_PRINT(WARNING, "Specified config file not found: ", filename);
121  }
122  wrapped = rrlib::xml::tDocument();
123  wrapped.AddRootNode(cXML_BRANCH_NAME);
124#endif
125}
126
127void tConfigFile::Append(const std::string& filename)
128{
129#ifdef _LIB_RRLIB_XML_PRESENT_
130  if (core::FinrocFileExists(filename))
131  {
132    // merge entries into first document
133    auto document = core::GetFinrocXMLDocument(filename, false); // false = do not validate with dtd
134    auto& root_node = document.RootNode();
135    for (auto it = root_node.ChildrenBegin(); it != root_node.ChildrenEnd(); ++it)
136    {
137      this->wrapped.RootNode().AddChildNode(*it, true); // not using copy resulted in erroneous behavior
138    }
139  }
140  else
141  {
142    throw std::runtime_error("Specified config file not found: " + filename);
143  }
144#endif
145}
146
147#ifdef _LIB_RRLIB_XML_PRESENT_
148rrlib::xml::tNode& tConfigFile::CreateEntry(const std::string& entry, bool leaf)
149{
150  if (!leaf)
151  {
152    std::vector<rrlib::xml::tNode*> result;
153    GetEntryImplementation(result, entry, wrapped.RootNode(), 0);
154    if (result.size() > 0)
155    {
156      // do we want to warn if node is a leaf node? - I currently do not think so
157      return *result[0];
158    }
159  }
160
161  size_t slash_index = entry.rfind('/');
162  tXMLNode& parent = (slash_index == std::string::npos || slash_index == 0) ? wrapped.RootNode() : CreateEntry(entry.substr(0, slash_index), false);
163  tXMLNode& created = parent.AddChildNode(leaf ? cXML_LEAF_NAME : cXML_BRANCH_NAME);
164  created.SetAttribute("name", (slash_index == std::string::npos) ? entry : entry.substr(slash_index + 1));
165  return created;
166}
167#endif
168
169tConfigFile* tConfigFile::Find(const core::tFrameworkElement& element)
170{
171  tConfigFile* config_file = element.GetAnnotation<tConfigFile>();
172  if (config_file && config_file->active == true)
173  {
174    return config_file;
175  }
176  core::tFrameworkElement* parent = element.GetParent();
177  if (parent)
178  {
179    return Find(*parent);
180  }
181  return NULL;
182}
183
184#ifdef _LIB_RRLIB_XML_PRESENT_
185rrlib::xml::tNode& tConfigFile::GetEntry(const std::string& entry, bool create)
186{
187  std::vector<rrlib::xml::tNode*> result;
188  GetEntryImplementation(result, entry, wrapped.RootNode(), 0);
189  if (result.size() > 1)
190  {
191    FINROC_LOG_PRINT(WARNING, "There are ", result.size(), " entries in config file with the qualified name '", entry, "'. Using the first one.");
192  }
193
194  if (!create)
195  {
196    if (result.size() == 0)
197    {
198      throw std::runtime_error("Config node not found: " + entry);
199    }
200    if (result[0]->Name() != cXML_LEAF_NAME)
201    {
202      throw std::runtime_error("Config node is no leaf: " + entry);
203    }
204    return *result[0];
205  }
206
207  // create node...
208  if (result.size() > 0)
209  {
210    // recreate existing node
211    std::string name = result[0]->GetStringAttribute("name");
212    tXMLNode& parent = result[0]->Parent();
213    parent.RemoveChildNode(*result[0]);
214    tXMLNode& new_node = parent.AddChildNode(cXML_LEAF_NAME);
215    new_node.SetAttribute("name", name);
216    return new_node;
217  }
218  else
219  {
220    return CreateEntry(entry, true);
221  }
222}
223
224std::vector<std::string> tConfigFile::GetChildrenNames(const std::string &entry)
225{
226  std::vector<rrlib::xml::tNode *> nodes;
227  this->GetEntryImplementation(nodes, entry, wrapped.RootNode(), 0);
228  if (nodes.size() > 1)
229  {
230    FINROC_LOG_PRINT(WARNING, "There are ", nodes.size(), " entries in config file with the qualified name '", entry, "'. Using the first one.");
231  }
232
233  if (nodes.size() == 0)
234  {
235    throw std::runtime_error("Config node not found: " + entry);
236  }
237  if (nodes[0]->Name() == cXML_LEAF_NAME)
238  {
239    throw std::runtime_error("Config node is a leaf: " + entry);
240  }
241
242  std::vector<std::string> result;
243  for (auto it = nodes.front()->ChildrenBegin(); it != nodes.front()->ChildrenEnd(); ++it)
244  {
245    result.push_back(it->GetStringAttribute("name"));
246  }
247  return result;
248}
249
250
251void tConfigFile::GetEntryImplementation(std::vector<rrlib::xml::tNode*>& result, const std::string& entry, rrlib::xml::tNode& node, size_t entry_string_index)
252{
253  if (entry_string_index >= entry.length())
254  {
255    return;
256  }
257
258  // Check for slash at beginning
259  if (entry[entry_string_index] == '/')
260  {
261    if (entry_string_index > 0)
262    {
263      FINROC_LOG_PRINT(WARNING, "Entry '", entry, "' seems to be not clean (sequential slashes). Skipping one slash now, as this is typically intended. Please fix this!");
264    }
265    entry_string_index++;
266  }
267
268  // Search child nodes
269  for (rrlib::xml::tNode::iterator child = node.ChildrenBegin(); child != node.ChildrenEnd(); ++child)
270  {
271    if (child->Name() == cXML_BRANCH_NAME || child->Name() == cXML_LEAF_NAME)
272    {
273      try
274      {
275        std::string name_attribute = child->GetStringAttribute("name");
276        if (entry.compare(entry_string_index, name_attribute.length(), name_attribute) == 0) // starts_with name attribute?
277        {
278          size_t new_entry_string_index = entry_string_index + name_attribute.length();
279          if (new_entry_string_index != entry.length())
280          {
281            if (entry[new_entry_string_index] == '/')
282            {
283              new_entry_string_index++;
284              GetEntryImplementation(result, entry, *child, new_entry_string_index);
285            }
286          }
287          else
288          {
289            result.push_back(&(*child));
290          }
291        }
292      }
293      catch (const std::exception& e)
294      {
295        FINROC_LOG_PRINT(WARNING, "Encountered tree node without name");
296      }
297    }
298  }
299
300  // Okay, we did not find any more
301}
302#endif
303
304std::string tConfigFile::GetStringEntry(const std::string& entry)
305{
306#ifdef _LIB_RRLIB_XML_PRESENT_
307  try
308  {
309    return GetEntry(entry).GetTextContent();
310  }
311  catch (const std::exception& e)
312  {
313    return "";
314  }
315#else
316  return "";
317#endif
318}
319
320bool tConfigFile::HasEntry(const std::string& entry)
321{
322#ifdef _LIB_RRLIB_XML_PRESENT_
323  // TODO: could be implemented more efficiently
324  try
325  {
326    GetEntry(entry);
327    return true;
328  }
329  catch (const std::exception& e)
330  {
331    return false;
332  }
333#else
334  return false;
335#endif
336}
337
338void tConfigFile::LoadParameterValues()
339{
340  LoadParameterValues(*GetAnnotated<core::tFrameworkElement>());
341}
342
343void tConfigFile::LoadParameterValues(core::tFrameworkElement& fe)
344{
345  rrlib::thread::tLock lock(fe.GetStructureMutex());  // nothing should change while we're doing this
346  for (auto it = fe.SubElementsBegin(true); it != fe.SubElementsEnd(); ++it)
347  {
348    if (it->IsPort() && it->IsReady() && Find(*it) == this)    // Does element belong to this configuration file?
349    {
350      internal::tParameterInfo* pi = it->GetAnnotation<internal::tParameterInfo>();
351      if (pi)
352      {
353        try
354        {
355          pi->LoadValue();
356        }
357        catch (const std::exception& e)
358        {
359          FINROC_LOG_PRINT_STATIC(ERROR, e);
360        }
361      }
362    }
363  }
364}
365
366void tConfigFile::SaveFile(const std::string& new_filename)
367{
368#ifdef _LIB_RRLIB_XML_PRESENT_
369  // first: update tree
370  core::tFrameworkElement* ann = GetAnnotated<core::tFrameworkElement>();
371  if (ann)
372  {
373    rrlib::thread::tLock lock(ann->GetStructureMutex()); // nothing should change while we're doing this
374    for (auto it = ann->SubElementsBegin(true); it != ann->SubElementsEnd(); ++it)
375    {
376      if (it->IsPort() && it->IsReady() && Find(*it) == this)    // Does element belong to this configuration file?
377      {
378        internal::tParameterInfo* pi = it->GetAnnotation<internal::tParameterInfo>();
379        if (pi)
380        {
381          try
382          {
383            pi->SaveValue();
384          }
385          catch (const std::exception& e)
386          {
387            FINROC_LOG_PRINT_STATIC(ERROR, e);
388          }
389        }
390      }
391    }
392  }
393
394  try
395  {
396    if (new_filename.length() > 0)
397    {
398      this->filename = new_filename;
399    }
400    std::string save_to = core::GetFinrocFileToSaveTo(this->filename);
401    if (save_to.length() == 0)
402    {
403      std::string save_to_alt = save_to;
404      std::replace(save_to_alt.begin(), save_to_alt.end(), '/', '_'); // Replace '/' characters with '_'
405      save_to_alt = core::GetFinrocFileToSaveTo(save_to_alt);
406      FINROC_LOG_PRINT(ERROR, "There does not seem to be any suitable location for: '", this->filename, "' . For now, using '", save_to_alt, "'.");
407      save_to = save_to_alt;
408    }
409
410    // write new tree to file
411    wrapped.WriteToFile(save_to);
412  }
413  catch (const std::exception& e)
414  {
415    FINROC_LOG_PRINT(ERROR, e);
416  }
417#endif
418}
419
420rrlib::serialization::tOutputStream& operator << (rrlib::serialization::tOutputStream& stream, const tConfigFile& config_file)
421{
422#ifdef _LIB_RRLIB_XML_PRESENT_
423  stream.WriteBoolean(config_file.IsActive());
424  stream.WriteString(config_file.GetFilename());
425
426  try
427  {
428    stream.WriteString(config_file.wrapped.RootNode().GetXMLDump());
429  }
430  catch (const std::exception& e)
431  {
432    FINROC_LOG_PRINT_STATIC(ERROR, e); // Should never occur
433  }
434#endif
435  return stream;
436}
437
438rrlib::serialization::tInputStream& operator >> (rrlib::serialization::tInputStream& stream, tConfigFile& config_file)
439{
440#ifdef _LIB_RRLIB_XML_PRESENT_
441  config_file.active = stream.ReadBoolean();
442  std::string file = stream.ReadString();
443  std::string content = stream.ReadString();
444
445  if (config_file.active && file.length() > 0 && content.length() == 0 && (file != config_file.filename))
446  {
447    // load file
448    if (core::FinrocFileExists(file))
449    {
450      try
451      {
452        config_file.wrapped = core::GetFinrocXMLDocument(file, false);
453      }
454      catch (const std::exception& e)
455      {
456        FINROC_LOG_PRINT_STATIC(ERROR, e);
457        config_file.wrapped = rrlib::xml::tDocument();
458        try
459        {
460          config_file.wrapped.AddRootNode(cXML_BRANCH_NAME);
461        }
462        catch (const std::exception& e1)
463        {
464          FINROC_LOG_PRINT_STATIC(ERROR, e1);
465        }
466      }
467    }
468    config_file.filename = file;
469  }
470  else if (config_file.active && content.length() > 0)
471  {
472    if (file.length() > 0)
473    {
474      config_file.filename = file;
475    }
476
477    try
478    {
479      config_file.wrapped = rrlib::xml::tDocument(content.c_str(), content.length() + 1);
480    }
481    catch (const std::exception& e)
482    {
483      FINROC_LOG_PRINT_STATIC(ERROR, e);
484    }
485  }
486#endif
487  return stream;
488}
489
490//----------------------------------------------------------------------
491// End of namespace declaration
492//----------------------------------------------------------------------
493}
494}
Note: See TracBrowser for help on using the repository browser.