Added cNameValueParser.

master
madmaxoft 2013-10-04 09:20:15 +02:00
parent e31343297e
commit 58f5ac84ab
2 changed files with 482 additions and 0 deletions

View File

@ -0,0 +1,412 @@
// NameValueParser.cpp
// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
#include "Globals.h"
#include "NameValueParser.h"
// DEBUG: Self-test
#if 0
class cNameValueParserTest
{
public:
cNameValueParserTest(void)
{
const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\"";
// Now try parsing char-by-char, to debug transitions across datachunk boundaries:
cNameValueParser Parser2;
for (int i = 0; i < sizeof(Data) - 1; i++)
{
Parser2.Parse(Data + i, 1);
}
Parser2.Finish();
// Parse as a single chunk of data:
cNameValueParser Parser(Data, sizeof(Data) - 1);
// Use the debugger to inspect the Parser variable
// Check that the two parsers have the same content:
for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
{
ASSERT(Parser2[itr->first] == itr->second);
} // for itr - Parser[]
// Try parsing in 2-char chunks:
cNameValueParser Parser3;
for (int i = 0; i < sizeof(Data) - 2; i += 2)
{
Parser3.Parse(Data + i, 2);
}
if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char
{
Parser3.Parse(Data + sizeof(Data) - 2, 1);
}
Parser3.Finish();
// Check that the third parser has the same content:
for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
{
ASSERT(Parser3[itr->first] == itr->second);
} // for itr - Parser[]
printf("cNameValueParserTest done");
}
} g_Test;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cNameValueParser:
cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) :
m_State(psKeySpace),
m_AllowsKeyOnly(a_AllowsKeyOnly)
{
}
cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) :
m_State(psKeySpace),
m_AllowsKeyOnly(a_AllowsKeyOnly)
{
Parse(a_Data, a_Size);
}
void cNameValueParser::Parse(const char * a_Data, int a_Size)
{
ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong!
if ((m_State == psInvalid) || (m_State == psFinished))
{
return;
}
int Last = 0;
for (int i = 0; i < a_Size;)
{
switch (m_State)
{
case psKeySpace:
{
// Skip whitespace until a non-whitespace is found, then start the key:
while ((i < a_Size) && (a_Data[i] <= ' '))
{
i++;
}
if ((i < a_Size) && (a_Data[i] > ' '))
{
m_State = psKey;
Last = i;
}
break;
}
case psKey:
{
// Read the key until whitespace or an equal sign:
while (i < a_Size)
{
if (a_Data[i] == '=')
{
m_CurrentKey.append(a_Data + Last, i - Last);
i++;
Last = i;
m_State = psEqual;
break;
}
else if (a_Data[i] <= ' ')
{
m_CurrentKey.append(a_Data + Last, i - Last);
i++;
Last = i;
m_State = psEqualSpace;
break;
}
else if (a_Data[i] == ';')
{
if (!m_AllowsKeyOnly)
{
m_State = psInvalid;
return;
}
m_CurrentKey.append(a_Data + Last, i - Last);
i++;
Last = i;
(*this)[m_CurrentKey] = "";
m_CurrentKey.clear();
m_State = psKeySpace;
break;
}
else if ((a_Data[i] == '\"') || (a_Data[i] == '\''))
{
m_State = psInvalid;
return;
}
i++;
} // while (i < a_Size)
if (i == a_Size)
{
// Still the key, ran out of data to parse, store the part of the key parsed so far:
m_CurrentKey.append(a_Data + Last, a_Size - Last);
return;
}
break;
}
case psEqualSpace:
{
// The space before the expected equal sign; the current key is already assigned
while (i < a_Size)
{
if (a_Data[i] == '=')
{
m_State = psEqual;
i++;
Last = i;
break;
}
else if (a_Data[i] == ';')
{
// Key-only
if (!m_AllowsKeyOnly)
{
m_State = psInvalid;
return;
}
i++;
Last = i;
(*this)[m_CurrentKey] = "";
m_CurrentKey.clear();
m_State = psKeySpace;
break;
}
else if (a_Data[i] > ' ')
{
m_State = psInvalid;
return;
}
i++;
} // while (i < a_Size)
break;
} // case psEqualSpace
case psEqual:
{
// just parsed the equal-sign
while (i < a_Size)
{
if (a_Data[i] == ';')
{
if (!m_AllowsKeyOnly)
{
m_State = psInvalid;
return;
}
i++;
Last = i;
(*this)[m_CurrentKey] = "";
m_CurrentKey.clear();
m_State = psKeySpace;
break;
}
else if (a_Data[i] == '\"')
{
i++;
Last = i;
m_State = psValueInDQuotes;
break;
}
else if (a_Data[i] == '\'')
{
i++;
Last = i;
m_State = psValueInSQuotes;
break;
}
else
{
m_CurrentValue.push_back(a_Data[i]);
i++;
Last = i;
m_State = psValueRaw;
break;
}
i++;
} // while (i < a_Size)
break;
} // case psEqual
case psValueInDQuotes:
{
while (i < a_Size)
{
if (a_Data[i] == '\"')
{
m_CurrentValue.append(a_Data + Last, i - Last);
(*this)[m_CurrentKey] = m_CurrentValue;
m_CurrentKey.clear();
m_CurrentValue.clear();
m_State = psAfterValue;
i++;
Last = i;
break;
}
i++;
} // while (i < a_Size)
if (i == a_Size)
{
m_CurrentValue.append(a_Data + Last, a_Size - Last);
}
break;
} // case psValueInDQuotes
case psValueInSQuotes:
{
while (i < a_Size)
{
if (a_Data[i] == '\'')
{
m_CurrentValue.append(a_Data + Last, i - Last);
(*this)[m_CurrentKey] = m_CurrentValue;
m_CurrentKey.clear();
m_CurrentValue.clear();
m_State = psAfterValue;
i++;
Last = i;
break;
}
i++;
} // while (i < a_Size)
if (i == a_Size)
{
m_CurrentValue.append(a_Data + Last, a_Size - Last);
}
break;
} // case psValueInSQuotes
case psValueRaw:
{
while (i < a_Size)
{
if (a_Data[i] == ';')
{
m_CurrentValue.append(a_Data + Last, i - Last);
(*this)[m_CurrentKey] = m_CurrentValue;
m_CurrentKey.clear();
m_CurrentValue.clear();
m_State = psKeySpace;
i++;
Last = i;
break;
}
i++;
}
if (i == a_Size)
{
m_CurrentValue.append(a_Data + Last, a_Size - Last);
}
break;
} // case psValueRaw
case psAfterValue:
{
// Between the closing DQuote or SQuote and the terminating semicolon
while (i < a_Size)
{
if (a_Data[i] == ';')
{
m_State = psKeySpace;
i++;
Last = i;
break;
}
else if (a_Data[i] < ' ')
{
i++;
continue;
}
m_State = psInvalid;
return;
} // while (i < a_Size)
break;
}
} // switch (m_State)
} // for i - a_Data[]
}
bool cNameValueParser::Finish(void)
{
switch (m_State)
{
case psInvalid:
{
return false;
}
case psFinished:
{
return true;
}
case psKey:
case psEqualSpace:
case psEqual:
{
if ((m_AllowsKeyOnly) && !m_CurrentKey.empty())
{
(*this)[m_CurrentKey] = "";
m_State = psFinished;
return true;
}
m_State = psInvalid;
return false;
}
case psValueRaw:
{
(*this)[m_CurrentKey] = m_CurrentValue;
m_State = psFinished;
return true;
}
case psValueInDQuotes:
case psValueInSQuotes:
{
// Missing the terminating quotes, this is an error
m_State = psInvalid;
return false;
}
case psKeySpace:
case psAfterValue:
{
m_State = psFinished;
return true;
}
}
ASSERT(!"Unhandled parser state!");
return false;
}

View File

@ -0,0 +1,70 @@
// NameValueParser.h
// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
#pragma once
class cNameValueParser :
public std::map<AString, AString>
{
public:
/// Creates an empty parser
cNameValueParser(bool a_AllowsKeyOnly = true);
/// Creates an empty parser, then parses the data given
cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true);
/// Parses the data given
void Parse(const char * a_Data, int a_Size);
/// Notifies the parser that no more data will be coming. Returns true if the parser state is valid
bool Finish(void);
/// Returns true if the data parsed so far was valid
bool IsValid(void) const { return (m_State != psInvalid); }
/// Returns true if the parser expects no more data
bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); }
protected:
enum eState
{
psKeySpace, ///< Parsing the space in front of the next key
psKey, ///< Currently adding more chars to the key in m_CurrentKey
psEqualSpace, ///< Space after m_CurrentKey
psEqual, ///< Just parsed the = sign after a name
psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign
psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign
psValueRaw, ///< Just parsed a raw value without a quote
psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end
psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped
psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data
} ;
/// The current state of the parser
eState m_State;
/// If true, the parser will accept keys without an equal sign and the value
bool m_AllowsKeyOnly;
/// Buffer for the current Key
AString m_CurrentKey;
/// Buffer for the current Value;
AString m_CurrentValue;
} ;