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
112unsigned long CYamlNode::GetULongValue(unsigned long nDefault) const
113{
114 try
115 {
116 if (! m_strValue.empty())
117 return stoul(m_strValue);
118 }
119 catch (...)
120 {
121 // Return default on conversion error
122 }
123 return nDefault;
124}
125
126double CYamlNode::GetDoubleValue(double dDefault) const
127{
128 try
129 {
130 if (! m_strValue.empty())
131 return stod(m_strValue);
132 }
133 catch (...)
134 {
135 // Return default on conversion error
136 }
137 return dDefault;
138}
139
140bool CYamlNode::GetBoolValue(bool bDefault) const
141{
142 if (m_strValue.empty())
143 return bDefault;
144
145 string strLower = m_strValue;
146 std::transform(strLower.begin(), strLower.end(), strLower.begin(), ::tolower);
147
148 if (strLower == "true" || strLower == "yes" || strLower == "y" || strLower == "1")
149 return true;
150 else if (strLower == "false" || strLower == "no" || strLower == "n" || strLower == "0")
151 return false;
152
153 return bDefault;
154}
155
156vector<string> CYamlNode::GetStringSequence() const
157{
158 vector<string> vecResult;
159 for (auto const& node : m_vecChildren)
160 {
161 vecResult.push_back(node.GetValue());
162 }
163 return vecResult;
164}
165
166//===============================================================================================================================
167// CYamlParser implementation
168//===============================================================================================================================
172
176
177bool CYamlParser::bParseFile(string const& strFileName)
178{
179 m_strFileName = strFileName;
180 m_nCurrentLine = 0;
181 m_strError.clear();
182
183 ifstream fileStream(strFileName);
184 if (! fileStream.is_open())
185 {
186 m_strError = "Cannot open file: " + strFileName;
187 return false;
188 }
189
190 try
191 {
192 m_RootNode = ParseSection(fileStream, -1);
193 }
194 catch (std::exception const& e)
195 {
196 m_strError = "Parse error at line " + std::to_string(m_nCurrentLine) + ": " + e.what();
197 return false;
198 }
199
200 fileStream.close();
201 return true;
202}
203
205{
206 return m_RootNode;
207}
208
210{
211 return m_strError;
212}
213
215{
216 return ! m_strError.empty();
217}
218
219int CYamlParser::nGetIndentLevel(string const& strLine) const
220{
221 int nIndent = 0;
222 for (char c : strLine)
223 {
224 if (c == ' ')
225 nIndent++;
226 else if (c == '\t')
227 nIndent += 4; // Treat tab as 4 spaces
228 else
229 break;
230 }
231 return nIndent;
232}
233
234string CYamlParser::strTrimLeft(string const& strLine) const
235{
236 auto it = strLine.begin();
237 while (it != strLine.end() && isspace(*it))
238 it++;
239 return string(it, strLine.end());
240}
241
242string CYamlParser::strTrimRight(string const& strLine) const
243{
244 auto it = strLine.rbegin();
245 while (it != strLine.rend() && isspace(*it))
246 it++;
247 return string(strLine.begin(), it.base());
248}
249
250string CYamlParser::strTrim(string const& strLine) const
251{
252 return strTrimLeft(strTrimRight(strLine));
253}
254
255bool CYamlParser::bIsComment(string const& strLine) const
256{
257 string strTrimmed = strTrimLeft(strLine);
258 return strTrimmed.empty() || strTrimmed[0] == '#';
259}
260
261bool CYamlParser::bIsEmpty(string const& strLine) const
262{
263 return strTrim(strLine).empty();
264}
265
266bool CYamlParser::bParseLine(string const& strLine, string& strKey, string& strValue, bool& bIsSequence) const
267{
268 string strTrimmed = strTrimLeft(strLine);
269 bIsSequence = false;
270
271 // Check for sequence item
272 if (strTrimmed.length() > 0 && strTrimmed[0] == '-')
273 {
274 bIsSequence = true;
275 strKey.clear();
276 strValue = strTrim(strTrimmed.substr(1));
277 strValue = strRemoveQuotes(strValue);
278 return true;
279 }
280
281 // Look for key-value pair
282 size_t nColonPos = strTrimmed.find(':');
283 if (nColonPos == string::npos)
284 return false;
285
286 strKey = strTrim(strTrimmed.substr(0, nColonPos));
287 if (nColonPos + 1 < strTrimmed.length())
288 strValue = strTrim(strTrimmed.substr(nColonPos + 1));
289 else
290 strValue.clear();
291
292 // Remove quotes from string values
293 strValue = strRemoveQuotes(strValue);
294
295 return true;
296}
297
298string CYamlParser::strRemoveQuotes(string const& strValue) const
299{
300 string result = strValue;
301
302 // Remove surrounding double quotes
303 if (result.length() >= 2 && result.front() == '"' && result.back() == '"')
304 {
305 result = result.substr(1, result.length() - 2);
306 }
307 // Remove surrounding single quotes
308 else if (result.length() >= 2 && result.front() == '\'' && result.back() == '\'')
309 {
310 result = result.substr(1, result.length() - 2);
311 }
312
313 return result;
314}
315
316CYamlNode CYamlParser::ParseSection(ifstream& fileStream, int nBaseIndent)
317{
318 CYamlNode currentNode;
319 string strLine;
320
321 while (getline(fileStream, strLine))
322 {
324
325 // Skip comments and empty lines
326 if (bIsComment(strLine) || bIsEmpty(strLine))
327 continue;
328
329 int nIndent = nGetIndentLevel(strLine);
330
331 // If we've gone back to a lower indentation level, we're done with this section
332 if (nBaseIndent >= 0 && nIndent <= nBaseIndent)
333 {
334 // Put the line back for the parent to process
335 fileStream.seekg(static_cast<int>(fileStream.tellg()) - static_cast<int>(strLine.length()) - 1);
337 break;
338 }
339
340 string strKey, strValue;
341 bool bIsSequence;
342
343 if (bParseLine(strLine, strKey, strValue, bIsSequence))
344 {
345 if (bIsSequence)
346 {
347 // Handle sequence item
348 CYamlNode itemNode;
349 if (! strValue.empty())
350 {
351 itemNode.SetValue(strValue);
352 }
353 else
354 {
355 // Multi-line sequence item
356 itemNode = ParseSection(fileStream, nIndent);
357 }
358 currentNode.AddSequenceItem(itemNode);
359 }
360 else
361 {
362 // Handle key-value pair
363 CYamlNode childNode;
364 if (! strValue.empty())
365 {
366 childNode.SetValue(strValue);
367 }
368 else
369 {
370 // Multi-line value
371 childNode = ParseSection(fileStream, nIndent);
372 }
373 currentNode.AddChild(strKey, childNode);
374 }
375 }
376 }
377
378 return currentNode;
379}
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
unsigned long GetULongValue(unsigned long nDefault=0) 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:73
string strTrim(string const &strLine) const
string m_strFileName
Definition yaml_parser.h:74
string strTrimRight(string const &strLine) const
int m_nCurrentLine
Definition yaml_parser.h:75
bool bParseLine(string const &strLine, string &strKey, string &strValue, bool &bIsSequence) const
string m_strError
Definition yaml_parser.h:76
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.