Teuchos - Trilinos Tools Package  Version of the Day
Teuchos_YamlParser.cpp
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // Teuchos: Common Tools Package
6 // Copyright (2004) Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact
39 // Jonathan Hu (jhu@sandia.gov)
40 // Andrey Prokopenko (aprokop@sandia.gov)
41 // Ray Tuminaro (rstumin@sandia.gov)
42 //
43 // ***********************************************************************
44 //
45 // @HEADER
46 
47 #ifndef TEUCHOS_YAMLPARSER_DEF_H_
48 #define TEUCHOS_YAMLPARSER_DEF_H_
49 
50 #include "Teuchos_YamlParser_decl.hpp"
52 
53 namespace Teuchos
54 {
55 
56 template<typename T> Teuchos::Array<T> getYamlArray(const YAML::Node& node)
57 {
59  for(YAML::const_iterator it = node.begin(); it != node.end(); it++)
60  {
61  arr.push_back(it->as<T>());
62  }
63  return arr;
64 }
65 
66 
67 /* Helper functions */
68 
69 void updateParametersFromYamlFile(const std::string& yamlFileName,
70  const Teuchos::Ptr<Teuchos::ParameterList>& paramList)
71 {
72  //load the YAML file in as a new param list
73  Teuchos::RCP<Teuchos::ParameterList> updated = YAMLParameterList::parseYamlFile(yamlFileName);
74  //now update the original list (overwriting values with same key)
75  paramList->setParameters(*updated);
76 }
77 
78 void updateParametersFromYamlCString(const char* const data,
79  const Teuchos::Ptr<Teuchos::ParameterList>& paramList,
80  bool overwrite)
81 {
82  Teuchos::RCP<Teuchos::ParameterList> updated = YAMLParameterList::parseYamlText(data);
83  if(overwrite)
84  {
85  paramList->setParameters(*updated);
86  }
87  else
88  {
89  paramList->setParametersNotAlreadySet(*updated);
90  }
91 }
92 
93 void updateParametersFromYamlString(const std::string& yamlData,
94  const Teuchos::Ptr<Teuchos::ParameterList>& paramList,
95  bool overwrite)
96 {
97  Teuchos::RCP<Teuchos::ParameterList> updated = YAMLParameterList::parseYamlText(yamlData);
98  if(overwrite)
99  {
100  paramList->setParameters(*updated);
101  }
102  else
103  {
104  paramList->setParametersNotAlreadySet(*updated);
105  }
106 }
107 
108 Teuchos::RCP<Teuchos::ParameterList> getParametersFromYamlFile(const std::string& yamlFileName)
109 {
110  return YAMLParameterList::parseYamlFile(yamlFileName);
111 }
112 
113 
114 std::string convertXmlToYaml(const std::string& xmlFileName)
115 {
116  //load the parameter list from xml
117  Teuchos::RCP<Teuchos::ParameterList> toConvert = Teuchos::getParametersFromXmlFile(xmlFileName);
118  //replace the file extension ".xml" with ".yaml", or append it if there was no extension
119  std::string yamlFileName;
120  if(xmlFileName.find(".xml") == std::string::npos)
121  {
122  yamlFileName = xmlFileName + ".yaml";
123  }
124  else
125  {
126  yamlFileName = xmlFileName.substr(0, xmlFileName.length() - 4) + ".yaml";
127  }
128  YAMLParameterList::writeYamlFile(yamlFileName, toConvert);
129  return yamlFileName;
130 }
131 
132 void convertXmlToYaml(const std::string& xmlFileName, const std::string& yamlFileName)
133 {
134  //load the parameter list from xml
135  Teuchos::RCP<Teuchos::ParameterList> toConvert = Teuchos::getParametersFromXmlFile(xmlFileName);
136  //replace the file extension ".xml" with ".yaml", or append it if there was no extension
137  YAMLParameterList::writeYamlFile(yamlFileName, toConvert);
138 }
139 
140 bool haveSameValuesUnordered(const Teuchos::ParameterList& lhs, const Teuchos::ParameterList& rhs, bool verbose)
141 {
143  Iter i = lhs.begin();
144  Iter j = rhs.begin();
145  if(lhs.name() != rhs.name())
146  {
147  if(verbose)
148  {
149  std::cout << "Parameter list names: \"" << lhs.name() << "\" and \"" << rhs.name() << "\".\n";
150  }
151  return false;
152  }
153  for(; i != lhs.end(); i++)
154  {
155  const std::string& key = lhs.name(i);
156  const Teuchos::ParameterEntry& val1 = lhs.entry(i);
157  //check that rhs also contains this key
158  if(!rhs.isParameter(key))
159  {
160  if(verbose)
161  {
162  std::cout << "One list is missing parameter: \"" << key << "\"\n";
163  }
164  return false;
165  }
166  const Teuchos::ParameterEntry& val2 = rhs.getEntry(key);
167  const Teuchos::any& any1 = val1.getAny(false);
168  const Teuchos::any& any2 = val2.getAny(false);
169  //check that types match
170  if(any1.type() != any2.type())
171  {
172  if(verbose)
173  {
174  std::cout << "Values for key \"" << key << "\" have different types.\n";
175  }
176  return false;
177  }
178  //check for parameter list special case (don't use operator==)
179  if(any1.type() == typeid(Teuchos::ParameterList))
180  {
181  if(!haveSameValuesUnordered(Teuchos::any_cast<Teuchos::ParameterList>(any1), Teuchos::any_cast<Teuchos::ParameterList>(any2), verbose))
182  {
183  //Don't need to print message here, the deepest list not matching will do that
184  return false;
185  }
186  }
187  else
188  {
189  //otherwise, use == to compare the values
190  if(!(val1 == val2))
191  {
192  if(verbose)
193  {
194  std::cout << "Values for key \"" << key << "\" are different.\n";
195  }
196  return false;
197  }
198  }
199  j++;
200  }
201  //lists must have same # of entries
202  if(j != rhs.end())
203  {
204  if(verbose)
205  {
206  std::cout << "Lists \"" << lhs.name() << "\" and \"" << rhs.name() << "\" have different number of parameters.\n";
207  }
208  return false;
209  }
210  return true;
211 }
212 
213 namespace YAMLParameterList
214 {
215 
216 Teuchos::RCP<Teuchos::ParameterList> parseYamlText(const std::string& text)
217 {
219  std::vector<YAML::Node> baseMap = YAML::LoadAll(text);
220  return readParams(baseMap);
221 }
222 
223 Teuchos::RCP<Teuchos::ParameterList> parseYamlText(const char* text)
224 {
226  std::vector<YAML::Node> baseMap = YAML::LoadAll(text);
227  return readParams(baseMap);
228 }
229 
230 Teuchos::RCP<Teuchos::ParameterList> parseYamlFile(const std::string& yamlFile)
231 {
232  std::vector<YAML::Node> baseMap = YAML::LoadAllFromFile(yamlFile);
233  return readParams(baseMap);
234 }
235 
236 Teuchos::RCP<Teuchos::ParameterList> readParams(std::vector<YAML::Node>& lists)
237 {
238  Teuchos::RCP<Teuchos::ParameterList> pl = rcp(new Teuchos::ParameterList); //pl is the root ParameterList to be returned
239  //If there is exactly one element in "lists", assume it is the anonymous top-level parameter list
240  //If there are more than one, place them all in the anonymous top-level list
241  for(size_t i = 0; i < lists.size(); i++)
242  {
243  processMapNode(lists[i], *pl, true);
244  }
245  return pl;
246 }
247 
248 void processMapNode(const YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel)
249 {
250  if(node.Type() != YAML::NodeType::Map)
251  {
252  throw YamlStructureError("All top-level elements of the YAML file must be maps.");
253  }
254  if(topLevel)
255  {
256  parent.setName("ANONYMOUS");
257  processMapNode(node.begin()->second, parent);
258  }
259  else
260  {
261  for(YAML::const_iterator i = node.begin(); i != node.end(); i++)
262  {
263  //make sure the key type is a string
264  if(i->first.Type() != YAML::NodeType::Scalar)
265  {
266  throw YamlKeyError("Keys must be plain strings");
267  }
268  //if this conversion fails and throws for any reason (shouldn't), let the caller handle it
269  const std::string key = i->first.as<std::string>();
270  processKeyValueNode(key, i->second, parent, topLevel);
271  }
272  }
273 }
274 
275 void processKeyValueNode(const std::string& key, const YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel)
276 {
277  //node (value) type can be a map (for nested param lists),
278  //a scalar (int, double, string), or a sequence of doubles (vector<double>)
279  if(node.Type() == YAML::NodeType::Scalar)
280  {
281  try
282  {
283  parent.set(key, node.as<int>());
284  }
285  catch(...)
286  {
287  try
288  {
289  parent.set(key, node.as<double>());
290  }
291  catch(...)
292  {
293  try
294  {
295  std::string rawString = node.as<std::string>();
296  if(rawString == "true")
297  {
298  parent.set<bool>(key, true);
299  }
300  else if(rawString == "false")
301  {
302  parent.set<bool>(key, false);
303  }
304  else
305  {
306  parent.set(key, rawString);
307  }
308  }
309  catch(...)
310  {
311  throw YamlScalarError("YAML scalars must be int, double, bool or string.");
312  }
313  }
314  }
315  }
316  else if(node.Type() == YAML::NodeType::Map)
317  {
318  if(topLevel)
319  {
320  processMapNode(node, parent);
321  }
322  else
323  {
324  Teuchos::ParameterList& sublist = parent.sublist(key);
325  processMapNode(node, sublist);
326  }
327  }
328  else if(node.Type() == YAML::NodeType::Sequence)
329  {
330  //typeString is used to provide a useful error message if types inconsistent
331  try
332  {
333  node.begin()->as<int>();
334  parent.set(key, getYamlArray<int>(node));
335  }
336  catch(...)
337  {
338  try
339  {
340  node.begin()->as<double>();
341  parent.set(key, getYamlArray<double>(node));
342  }
343  catch(...)
344  {
345  try
346  {
347  node.begin()->as<std::string>();
348  parent.set(key, getYamlArray<std::string>(node));
349  }
350  catch(...)
351  {
352  throw YamlSequenceError(std::string("Array \"") + key + "\" must contain int, double, bool or string");
353  }
354  }
355  }
356  }
357  else if(node.Type() == YAML::NodeType::Null)
358  {
359  //treat NULL as empty string (not an error)
360  parent.set(key, std::string());
361  }
362  else
363  {
364  //Undefined
365  throw YamlUndefinedNodeError("Value type in a key-value pair must be one of: int, double, string, array, sublist.");
366  }
367 }
368 
369 
370 
371 void writeYamlFile(const std::string& yamlFile, Teuchos::RCP<Teuchos::ParameterList>& pl)
372 {
373  std::ofstream yaml(yamlFile);
374  yaml << "%YAML 1.1\n---\n";
375  yaml << "ANONYMOUS:"; //original top-level list name is not stored by ParameterList
376  if(pl->numParams() == 0)
377  {
378  yaml << " { }\n";
379  }
380  else
381  {
382  writeParameterList(*pl, yaml, 2);
383  }
384  yaml << "...";
385 }
386 
387 void writeParameterList(Teuchos::ParameterList& pl, std::ofstream& yaml, int indentLevel)
388 {
389  if(pl.begin() == pl.end())
390  {
391  yaml << "{ }\n";
392  }
393  else
394  {
395  yaml << '\n';
396  for(PLIter it = pl.begin(); it != pl.end(); it++)
397  {
398  writeParameter(pl.name(it), pl.entry(it), yaml, indentLevel);
399  }
400  }
401 }
402 
403 void writeParameter(const std::string& paramName, const Teuchos::ParameterEntry& entry, std::ofstream& yaml, int indentLevel)
404 {
405  for(int i = 0; i < indentLevel; i++)
406  {
407  yaml << ' ';
408  }
409  generalWriteString(paramName, yaml);
410  yaml << ": ";
411  if(entry.isList())
412  {
413  writeParameterList(Teuchos::getValue<Teuchos::ParameterList>(entry), yaml, indentLevel + 2);
414  return;
415  }
416  else if(entry.isArray())
417  {
418  yaml << '[';
419  if(entry.isType<Teuchos::Array<int> >())
420  {
421  Teuchos::Array<int>& arr = Teuchos::getValue<Teuchos::Array<int> >(entry);
422  for(int i = 0; i < arr.size(); i++)
423  {
424  yaml << arr[i];
425  if(i != arr.size() - 1)
426  yaml << ", ";
427  }
428  }
429  else if(entry.isType<Teuchos::Array<double> >())
430  {
431  Teuchos::Array<double>& arr = Teuchos::getValue<Teuchos::Array<double> >(entry);
432  for(int i = 0; i < arr.size(); i++)
433  {
434  generalWriteDouble(arr[i], yaml);
435  if(i != arr.size() - 1)
436  yaml << ", ";
437  }
438  }
439  else if(entry.isType<Teuchos::Array<std::string> >())
440  {
441  Teuchos::Array<std::string>& arr = Teuchos::getValue<Teuchos::Array<std::string> >(entry);
442  for(int i = 0; i < arr.size(); i++)
443  {
444  generalWriteString(arr[i], yaml);
445  if(i != arr.size() - 1)
446  yaml << ", ";
447  }
448  }
449  yaml << ']';
450  }
451  else if(entry.isType<int>())
452  {
453  yaml << Teuchos::getValue<int>(entry);
454  }
455  else if(entry.isType<double>())
456  {
457  generalWriteDouble(Teuchos::getValue<double>(entry), yaml);
458  }
459  else if(entry.isType<std::string>())
460  {
461  std::string& str = Teuchos::getValue<std::string>(entry);
462  if(strchr(str.c_str(), '\n'))
463  {
464  //need explicit indentation so that indentation in the string is preserved
465  yaml << "|2-\n";
466  //for each line, apply indent then print the line verbatim
467  size_t index = 0;
468  while(true)
469  {
470  size_t next = str.find('\n', index);
471  for(int i = 0; i < indentLevel + 2; i++)
472  {
473  yaml << ' ';
474  }
475  if(next == std::string::npos)
476  {
477  yaml << str.substr(index, std::string::npos);
478  break;
479  }
480  else
481  {
482  yaml << str.substr(index, next - index) << '\n';
483  }
484  index = next + 1;
485  }
486  }
487  else
488  {
489  generalWriteString(str, yaml);
490  }
491  }
492  else if(entry.isType<bool>())
493  {
494  yaml << (Teuchos::getValue<bool>(entry) ? "true" : "false");
495  }
496  yaml << '\n';
497 }
498 
499 void generalWriteString(const std::string& str, std::ofstream& yaml)
500 {
501  if(stringNeedsQuotes(str))
502  {
503  yaml << '\'' << str << '\'';
504  }
505  else
506  {
507  yaml << str;
508  }
509 }
510 
511 void generalWriteDouble(double d, std::ofstream& yaml)
512 {
513  yaml << std::showpoint << std::setprecision(8);
514  if(d < 1e6 && d > 1e-5)
515  {
516  //use regular notation
517  yaml << d;
518  }
519  else
520  {
521  yaml << std::scientific << d;
522  }
523  yaml << std::fixed;
524 }
525 
526 bool stringNeedsQuotes(const std::string& str)
527 {
528  return strpbrk(str.c_str(), ":{}[],&*#?|-<>=!%@\\");
529 }
530 
531 } //namespace YAMLParameterList
532 
533 } //namespace Teuchos
534 
535 #endif
C++ Standard Library compatable filtered iterator.
ConstIterator begin() const
An iterator pointing to the first entry.
ParameterList & set(std::string const &name, T const &value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
Set a parameter whose value has type T.
This object is held as the "value" in the Teuchos::ParameterList std::map.
const ParameterEntry & entry(ConstIterator i) const
Access to ParameterEntry (i.e., returns i->second)
Ordinal numParams() const
Get the number of stored parameters.
Modified boost::any class, which is a container for a templated value.
Definition: Teuchos_any.hpp:86
Simple helper functions that make it easy to read and write XML to and from a parameterlist.
ConstIterator end() const
An iterator pointing beyond the last entry.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
size_type size() const
bool isList() const
Return whether or not the value itself is a list.
A list of parameters of arbitrary type.
ParameterList & setParameters(const ParameterList &source)
bool isType() const
Test the type of the data being contained.
const std::type_info & type() const
Return the type of value being stored.
const std::string & name() const
The name of this ParameterList.
any & getAny(bool activeQry=true)
Direct access to the Teuchos::any data value underlying this object. The bool argument activeQry (def...
void push_back(const value_type &x)
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos, as well as a number of utility routines.
ParameterList & setParametersNotAlreadySet(const ParameterList &source)
bool isParameter(const std::string &name) const
Whether the given parameter exists in this list.
bool isArray() const
Test if the type of data being contained is a Teuchos::Array.
ParameterList & sublist(const std::string &name, bool mustAlreadyExist=false, const std::string &docString="")
Creates an empty sublist and returns a reference to the sublist name. If the list already exists...
ParameterList & setName(const std::string &name)
Set the name of *this list.
ParameterEntry & getEntry(const std::string &name)
Retrieves an entry with the name name.
Simple wrapper class for raw pointers to single objects where no persisting relationship exists...
Replacement for std::vector that is compatible with the Teuchos Memory Management classes...