CoastalME (Coastal Modelling Environment)
Simulates the long-term behaviour of complex coastlines
Loading...
Searching...
No Matches
yaml_parser.cpp
Go to the documentation of this file.
1
12
13/* ==============================================================================================================================
14
15 This file is part of CoastalME, the Coastal Modelling Environment.
16
17 CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23==============================================================================================================================*/
24#include "yaml_parser.h"
25#include <iostream>
26#include <sstream>
27#include <algorithm>
28#include <cctype>
29
30using std::cerr;
31using std::endl;
32using std::getline;
33using std::isspace;
34using std::stod;
35using std::stoi;
36using std::stringstream;
37
38//===============================================================================================================================
39// CYamlNode implementation
40//===============================================================================================================================
44
48
49void CYamlNode::SetValue(string const& strValue)
50{
51 m_strValue = strValue;
52}
53
54void CYamlNode::AddChild(string const& strKey, CYamlNode const& node)
55{
56 m_mapChildren[strKey] = node;
57}
58
60{
61 m_vecChildren.push_back(node);
62 m_bIsSequence = true;
63}
64
65string CYamlNode::GetValue() const
66{
67 return m_strValue;
68}
69
70bool CYamlNode::HasChild(string const& strKey) const
71{
72 return m_mapChildren.find(strKey) != m_mapChildren.end();
73}
74
75CYamlNode CYamlNode::GetChild(string const& strKey) const
76{
77 auto it = m_mapChildren.find(strKey);
78 if (it != m_mapChildren.end())
79 return it->second;
80 return CYamlNode(); // Return empty node if not found
81}
82
83vector<CYamlNode> CYamlNode::GetSequence() const
84{
85 return m_vecChildren;
86}
87
89{
90 return m_bIsSequence;
91}
92
94{
95 return static_cast<int>(m_vecChildren.size());
96}
97
98int CYamlNode::GetIntValue(int nDefault) const
99{
100 try
101 {
102 if (! m_strValue.empty())
103 return stoi(m_strValue);
104 }
105 catch (...)
106 {
107 // Return default on conversion error
108 }
109 return nDefault;
110}
111
112double CYamlNode::GetDoubleValue(double dDefault) const
113{
114 try
115 {
116 if (! m_strValue.empty())
117 return stod(m_strValue);
118 }
119 catch (...)
120 {
121 // Return default on conversion error
122 }
123 return dDefault;
124}
125
126bool CYamlNode::GetBoolValue(bool bDefault) const
127{
128 if (m_strValue.empty())
129 return bDefault;
130
131 string strLower = m_strValue;
132 std::transform(strLower.begin(), strLower.end(), strLower.begin(), ::tolower);
133
134 if (strLower == "true" || strLower == "yes" || strLower == "y" || strLower == "1")
135 return true;
136 else if (strLower == "false" || strLower == "no" || strLower == "n" || strLower == "0")
137 return false;
138
139 return bDefault;
140}
141
142vector<string> CYamlNode::GetStringSequence() const
143{
144 vector<string> vecResult;
145 for (auto const& node : m_vecChildren)
146 {
147 vecResult.push_back(node.GetValue());
148 }
149 return vecResult;
150}
151
152//===============================================================================================================================
153// CYamlParser implementation
154//===============================================================================================================================
158
162
163bool CYamlParser::bParseFile(string const& strFileName)
164{
165 m_strFileName = strFileName;
166 m_nCurrentLine = 0;
167 m_strError.clear();
168
169 ifstream fileStream(strFileName);
170 if (! fileStream.is_open())
171 {
172 m_strError = "Cannot open file: " + strFileName;
173 return false;
174 }
175
176 try
177 {
178 m_RootNode = ParseSection(fileStream, -1);
179 }
180 catch (std::exception const& e)
181 {
182 m_strError = "Parse error at line " + std::to_string(m_nCurrentLine) + ": " + e.what();
183 return false;
184 }
185
186 fileStream.close();
187 return true;
188}
189
191{
192 return m_RootNode;
193}
194
196{
197 return m_strError;
198}
199
201{
202 return ! m_strError.empty();
203}
204
205int CYamlParser::nGetIndentLevel(string const& strLine) const
206{
207 int nIndent = 0;
208 for (char c : strLine)
209 {
210 if (c == ' ')
211 nIndent++;
212 else if (c == '\t')
213 nIndent += 4; // Treat tab as 4 spaces
214 else
215 break;
216 }
217 return nIndent;
218}
219
220string CYamlParser::strTrimLeft(string const& strLine) const
221{
222 auto it = strLine.begin();
223 while (it != strLine.end() && isspace(*it))
224 it++;
225 return string(it, strLine.end());
226}
227
228string CYamlParser::strTrimRight(string const& strLine) const
229{
230 auto it = strLine.rbegin();
231 while (it != strLine.rend() && isspace(*it))
232 it++;
233 return string(strLine.begin(), it.base());
234}
235
236string CYamlParser::strTrim(string const& strLine) const
237{
238 return strTrimLeft(strTrimRight(strLine));
239}
240
241bool CYamlParser::bIsComment(string const& strLine) const
242{
243 string strTrimmed = strTrimLeft(strLine);
244 return strTrimmed.empty() || strTrimmed[0] == '#';
245}
246
247bool CYamlParser::bIsEmpty(string const& strLine) const
248{
249 return strTrim(strLine).empty();
250}
251
252bool CYamlParser::bParseLine(string const& strLine, string& strKey, string& strValue, bool& bIsSequence) const
253{
254 string strTrimmed = strTrimLeft(strLine);
255 bIsSequence = false;
256
257 // Check for sequence item
258 if (strTrimmed.length() > 0 && strTrimmed[0] == '-')
259 {
260 bIsSequence = true;
261 strKey.clear();
262 strValue = strTrim(strTrimmed.substr(1));
263 strValue = strRemoveQuotes(strValue);
264 return true;
265 }
266
267 // Look for key-value pair
268 size_t nColonPos = strTrimmed.find(':');
269 if (nColonPos == string::npos)
270 return false;
271
272 strKey = strTrim(strTrimmed.substr(0, nColonPos));
273 if (nColonPos + 1 < strTrimmed.length())
274 strValue = strTrim(strTrimmed.substr(nColonPos + 1));
275 else
276 strValue.clear();
277
278 // Remove quotes from string values
279 strValue = strRemoveQuotes(strValue);
280
281 return true;
282}
283
284string CYamlParser::strRemoveQuotes(string const& strValue) const
285{
286 string result = strValue;
287
288 // Remove surrounding double quotes
289 if (result.length() >= 2 && result.front() == '"' && result.back() == '"')
290 {
291 result = result.substr(1, result.length() - 2);
292 }
293 // Remove surrounding single quotes
294 else if (result.length() >= 2 && result.front() == '\'' && result.back() == '\'')
295 {
296 result = result.substr(1, result.length() - 2);
297 }
298
299 return result;
300}
301
302CYamlNode CYamlParser::ParseSection(ifstream& fileStream, int nBaseIndent)
303{
304 CYamlNode currentNode;
305 string strLine;
306
307 while (getline(fileStream, strLine))
308 {
310
311 // Skip comments and empty lines
312 if (bIsComment(strLine) || bIsEmpty(strLine))
313 continue;
314
315 int nIndent = nGetIndentLevel(strLine);
316
317 // If we've gone back to a lower indentation level, we're done with this section
318 if (nBaseIndent >= 0 && nIndent <= nBaseIndent)
319 {
320 // Put the line back for the parent to process
321 fileStream.seekg(static_cast<int>(fileStream.tellg()) - static_cast<int>(strLine.length()) - 1);
323 break;
324 }
325
326 string strKey, strValue;
327 bool bIsSequence;
328
329 if (bParseLine(strLine, strKey, strValue, bIsSequence))
330 {
331 if (bIsSequence)
332 {
333 // Handle sequence item
334 CYamlNode itemNode;
335 if (! strValue.empty())
336 {
337 itemNode.SetValue(strValue);
338 }
339 else
340 {
341 // Multi-line sequence item
342 itemNode = ParseSection(fileStream, nIndent);
343 }
344 currentNode.AddSequenceItem(itemNode);
345 }
346 else
347 {
348 // Handle key-value pair
349 CYamlNode childNode;
350 if (! strValue.empty())
351 {
352 childNode.SetValue(strValue);
353 }
354 else
355 {
356 // Multi-line value
357 childNode = ParseSection(fileStream, nIndent);
358 }
359 currentNode.AddChild(strKey, childNode);
360 }
361 }
362 }
363
364 return currentNode;
365}
Simple YAML node class to represent parsed values.
Definition yaml_parser.h:39
map< string, CYamlNode > m_mapChildren
Definition yaml_parser.h:42
string m_strValue
Definition yaml_parser.h:41
bool m_bIsSequence
Definition yaml_parser.h:44
bool IsSequence() const
int GetSequenceSize() const
vector< string > GetStringSequence() const
vector< CYamlNode > m_vecChildren
Definition yaml_parser.h:43
string GetValue() const
vector< CYamlNode > GetSequence() const
bool HasChild(string const &strKey) const
void AddChild(string const &strKey, CYamlNode const &node)
bool GetBoolValue(bool bDefault=false) const
void AddSequenceItem(CYamlNode const &node)
double GetDoubleValue(double dDefault=0.0) const
CYamlNode GetChild(string const &strKey) const
int GetIntValue(int nDefault=0) const
void SetValue(string const &strValue)
string strRemoveQuotes(string const &strValue) const
CYamlNode m_RootNode
Definition yaml_parser.h:72
string strTrim(string const &strLine) const
string m_strFileName
Definition yaml_parser.h:73
string strTrimRight(string const &strLine) const
int m_nCurrentLine
Definition yaml_parser.h:74
bool bParseLine(string const &strLine, string &strKey, string &strValue, bool &bIsSequence) const
string m_strError
Definition yaml_parser.h:75
string GetError() const
bool bIsComment(string const &strLine) const
string strTrimLeft(string const &strLine) const
CYamlNode ParseSection(ifstream &fileStream, int nBaseIndent)
bool bParseFile(string const &strFileName)
bool bHasError() const
int nGetIndentLevel(string const &strLine) const
CYamlNode GetRoot() const
bool bIsEmpty(string const &strLine) const
Simple YAML parser for CoastalME configuration files.