Important Update: (incomplete)
- Split CConfigCore into two classes: - DataTreeCore.h - Tree data structure and access - JSONparseCore.h - JSON parsing functionality - Renamed TConfigParam struct to TDataMember - Renamed most of CDataTreeCore methods
This commit is contained in:
515
JSONparseCore.cpp
Normal file
515
JSONparseCore.cpp
Normal file
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* JSONparseCore.cpp
|
||||
*
|
||||
* Created on: 5 Mar 2017
|
||||
* Author: wentzelc
|
||||
*/
|
||||
|
||||
// redA Libraries
|
||||
#include "JSONparseCore.h"
|
||||
|
||||
// Standard C/C++ Libraries
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
CJSONparse::CJSONparse( CDataTree * pDataTree )
|
||||
{
|
||||
// Parameter tree
|
||||
DataTree = pDataTree;
|
||||
|
||||
// File Operation
|
||||
InputHandle = -1;
|
||||
OutputHandle = -1;
|
||||
|
||||
Buffer = NULL;
|
||||
BufEnd = NULL;
|
||||
Level = 0;
|
||||
|
||||
// Parsing operation
|
||||
BufPos = NULL;
|
||||
LineMark = NULL;
|
||||
LineNo = 0;
|
||||
|
||||
// Printing operation
|
||||
Spacer[0] = 0;
|
||||
SpacerLen = 0;
|
||||
|
||||
// Error reporting
|
||||
Error = false;
|
||||
ErrorText[0] = 0;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
CJSONparse::~CJSONparse()
|
||||
{
|
||||
// Destroy buffer
|
||||
if (Buffer) {
|
||||
delete Buffer;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::LoadFile( const char * FilePath, int pBufLen )
|
||||
{
|
||||
TDataMember * RootObject = NULL;
|
||||
|
||||
// Validate
|
||||
if (!FilePath || !FilePath[0]) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "No File path specified" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open file
|
||||
if (!(InputHandle = open( FilePath, O_RDONLY ))) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Could not open file" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load Buffer
|
||||
CreateBuffer( pBufLen );
|
||||
if (!FillBuffer()) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Could not read from file" );
|
||||
FreeBuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset values
|
||||
LineNo = 1;
|
||||
Error = false;
|
||||
|
||||
// Create Root object
|
||||
DataTree->DeleteAll();
|
||||
DataTree->GetParent( NULL, &RootObject );
|
||||
Level = 0;
|
||||
|
||||
// Parse Root Object
|
||||
SkipWhiteSpace();
|
||||
if (!ParseObject( RootObject )) {
|
||||
if (!Error) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "First entry in file must be an Object on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
}
|
||||
FreeBuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure remainder of file is empty
|
||||
SkipWhiteSpace();
|
||||
if (*BufPos != 0) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "No content expected after Root object on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
FreeBuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
close( InputHandle );
|
||||
FreeBuffer();
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::CreateBuffer( int pBufLen )
|
||||
{
|
||||
// Validate
|
||||
if (!pBufLen)
|
||||
return false;
|
||||
|
||||
// Create buffer
|
||||
Buffer = new CShiftBuffer( pBufLen );
|
||||
|
||||
// Reset markers
|
||||
Buffer->PeekDirect( &BufPos, 0 );
|
||||
BufEnd = BufPos;
|
||||
LineMark = BufPos;
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::FillBuffer()
|
||||
{
|
||||
// Read from file
|
||||
Buffer->ReadFromFD( InputHandle );
|
||||
|
||||
// Update Markers
|
||||
Buffer->PeekDirect( &BufEnd, Buffer->Len() );
|
||||
Buffer->PeekDirect( &BufPos, 0 );
|
||||
LineMark = BufPos;
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void CJSONparse::FreeBuffer()
|
||||
{
|
||||
// Destroy buffer
|
||||
if (Buffer) {
|
||||
delete Buffer;
|
||||
Buffer = NULL;
|
||||
|
||||
// Update Markers
|
||||
BufEnd = NULL;
|
||||
BufPos = NULL;
|
||||
LineMark = NULL;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::ParseObject( TDataMember * Object )
|
||||
{
|
||||
TDataMember ** ParamPtr = NULL;
|
||||
TDataMember * Param = NULL;
|
||||
char * ParamName = NULL;
|
||||
int Len = 0;
|
||||
|
||||
// Clear Values
|
||||
Error = false;
|
||||
|
||||
// Check for start of Object
|
||||
if (*BufPos != '{') {
|
||||
return false;
|
||||
}
|
||||
BufPos++;
|
||||
|
||||
// Set Type
|
||||
Level++;
|
||||
DataTree->SetMember( Object, jtObject, NULL, -1 );
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Look for Param Name
|
||||
SkipWhiteSpace();
|
||||
if (*BufPos == '}') {
|
||||
break;
|
||||
}
|
||||
else if (!ParseString( &ParamName, &Len )) {
|
||||
if (!Error) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Expect quoted key name on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (!Len) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Empty parameter name on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if Param exists
|
||||
ParamPtr = &(Object->FirstChild);
|
||||
while (*ParamPtr && strcasecmp( (*ParamPtr)->Name, ParamName )) {
|
||||
ParamPtr = &((*ParamPtr)->Next);
|
||||
}
|
||||
// If not exist, add to end of list
|
||||
if (!*ParamPtr) {
|
||||
*ParamPtr = DataTree->CreateMember( NULL );
|
||||
(*ParamPtr)->Name = ParamName;
|
||||
}
|
||||
Param = *ParamPtr;
|
||||
|
||||
// Check for delimiter
|
||||
SkipWhiteSpace();
|
||||
if (*BufPos != ':') {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Expected ':' delimiter on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
return false;
|
||||
}
|
||||
BufPos++;
|
||||
|
||||
// Get Value
|
||||
SkipWhiteSpace();
|
||||
if (!ParseObject( Param ) &&
|
||||
!Error &&
|
||||
!ParseString( &Param->Value, &Param->Len, &Param->Type ) &&
|
||||
!Error &&
|
||||
!ParsePrimitive( &Param->Value, &Param->Len, &Param->Type )) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// One item added
|
||||
Object->Len++;
|
||||
|
||||
// Check if more parameters to follow
|
||||
SkipWhiteSpace();
|
||||
if (*BufPos != ',') {
|
||||
// No more parameters
|
||||
break;
|
||||
}
|
||||
BufPos++;
|
||||
}
|
||||
|
||||
// Expect end of object
|
||||
if (*BufPos != '}') {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Closing brace for object '}' expected on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
return false;
|
||||
}
|
||||
BufPos++;
|
||||
Level--;
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::ParseString( char ** Value, int *pLen, EDataType *pType )
|
||||
{
|
||||
char * Mark;
|
||||
char * EndMark;
|
||||
int Len;
|
||||
|
||||
// Clear value
|
||||
Error = false;
|
||||
*Value = NULL;
|
||||
Len = 0;
|
||||
|
||||
// Check for opening quote
|
||||
if (*BufPos != '"') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark start of quote
|
||||
Mark = BufPos;
|
||||
BufPos++;
|
||||
|
||||
// Check for closing quote
|
||||
while ((BufPos = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" )))
|
||||
{
|
||||
if (*BufPos == '"') {
|
||||
// End of string found
|
||||
BufPos++;
|
||||
break;
|
||||
}
|
||||
else if (!*BufPos) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Expect closing '\"' for string on line %d:%ld", LineNo, Mark-LineMark );
|
||||
return false;
|
||||
}
|
||||
else if (*BufPos == '\\') {
|
||||
if (*(BufPos+1) == 'u') {
|
||||
for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) {
|
||||
if (!isxdigit( *EndMark )) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%ld", LineNo, Mark-LineMark );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BufPos += 6;
|
||||
}
|
||||
else if (strchr( "bfnrt/\\\"", *(BufPos+1) )) {
|
||||
BufPos += 2;
|
||||
}
|
||||
else {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Invalid escape sequence on line %d:%ld", LineNo, Mark-LineMark );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Un-escaped special character in string on line %d:%ld", LineNo, BufPos-LineMark );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate size of Param name
|
||||
Len = BufPos-Mark-2;
|
||||
|
||||
// Create Return value pointer
|
||||
*Value = (char*)malloc( Len+1 );
|
||||
memcpy( *Value, Mark+1, Len );
|
||||
(*Value)[Len] = 0;
|
||||
|
||||
// Set other parameters
|
||||
if (pType)
|
||||
*pType = jtString;
|
||||
if (pLen)
|
||||
*pLen = Len;
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::ParsePrimitive( char ** Value, int * pLen, EDataType * pType )
|
||||
{
|
||||
char * Mark;
|
||||
char * EndMark;
|
||||
int Len;
|
||||
|
||||
// Clear value
|
||||
Error = false;
|
||||
*Value = NULL;
|
||||
Len = 0;
|
||||
|
||||
// Mark start of value
|
||||
Mark = BufPos;
|
||||
|
||||
// Get end of value
|
||||
while ((*BufPos != 0) && !isspace(*BufPos) && (*BufPos != ',')) {
|
||||
BufPos++;
|
||||
}
|
||||
|
||||
// Check length of value
|
||||
Len = BufPos - Mark;
|
||||
if (!Len) {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Missing param value on line %d:%ld", LineNo, Mark-LineMark );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for primitive values
|
||||
if ((Len == 4) && !strncasecmp( Mark, "null", 4 )) {
|
||||
if (pType)
|
||||
*pType = jtNull;
|
||||
if (pLen)
|
||||
*pLen = Len;
|
||||
*Value = (char*)malloc( 5 );
|
||||
strcpy( *Value, "null" );
|
||||
}
|
||||
else if ((Len == 4) && !strncasecmp( Mark, "true", 4 )) {
|
||||
if (pType)
|
||||
*pType = jtBool;
|
||||
if (pLen)
|
||||
*pLen = Len;
|
||||
*Value = (char*)malloc( 5 );
|
||||
strcpy( *Value, "true" );
|
||||
}
|
||||
else if ((Len == 5) && !strncasecmp( Mark, "false", 5 )) {
|
||||
if (pType)
|
||||
*pType = jtBool;
|
||||
if (pLen)
|
||||
*pLen = Len;
|
||||
*Value = (char*)malloc( 6 );
|
||||
strcpy( *Value, "false" );
|
||||
}
|
||||
else {
|
||||
// Try conversion to int
|
||||
strtol( Mark, &EndMark, 10 );
|
||||
if (EndMark == BufPos) {
|
||||
if (pType)
|
||||
*pType = jtInt;
|
||||
if (pLen)
|
||||
*pLen = Len;
|
||||
*Value = (char*)malloc( Len+1 );
|
||||
memcpy( *Value, Mark, Len );
|
||||
(*Value)[Len] = 0;
|
||||
}
|
||||
else {
|
||||
// Try conversion to float
|
||||
strtod( Mark, &EndMark );
|
||||
if (EndMark == BufPos) {
|
||||
if (pType)
|
||||
*pType = jtFloat;
|
||||
if (pLen)
|
||||
*pLen = Len;
|
||||
*Value = (char*)malloc( Len+1 );
|
||||
memcpy( *Value, Mark, Len );
|
||||
(*Value)[Len] = 0;
|
||||
}
|
||||
else {
|
||||
Error = true;
|
||||
sprintf( ErrorText, "Invalid primitive param value on line %d:%ld", LineNo, Mark-LineMark );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::SaveFile( const char * FilePath, const int Indent )
|
||||
{
|
||||
TDataMember * RootObject;
|
||||
|
||||
// Validate
|
||||
if (!FilePath || !FilePath[0])
|
||||
return false;
|
||||
|
||||
// Open file
|
||||
if (!(OutputHandle = open( FilePath, O_WRONLY )))
|
||||
return false;
|
||||
|
||||
// Save Root object
|
||||
Level = 0;
|
||||
DataTree->GetParent( NULL, &RootObject );
|
||||
SaveObject( RootObject, Indent );
|
||||
|
||||
// Close file
|
||||
close( OutputHandle );
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CJSONparse::SaveObject( TDataMember * Object, const int Indent )
|
||||
{
|
||||
TDataMember * Param;
|
||||
|
||||
// Opening brace
|
||||
dprintf( OutputHandle, "{\n" );
|
||||
|
||||
// Extend spacer
|
||||
Level++;
|
||||
memset( &Spacer[SpacerLen], ' ', 2 );
|
||||
SpacerLen += 2;
|
||||
Spacer[SpacerLen] = 0;
|
||||
|
||||
// Save parameters
|
||||
for (Param = Object->FirstChild; Param != NULL; (Param = Param->Next))
|
||||
{
|
||||
// Write parameter name
|
||||
dprintf( OutputHandle, "%s\"%s\": ", Spacer, Param->Name );
|
||||
|
||||
switch (Param->Type)
|
||||
{
|
||||
case jtNull :
|
||||
dprintf( OutputHandle, "%s,\n", "null" );
|
||||
break;
|
||||
|
||||
case jtBool :
|
||||
case jtInt :
|
||||
case jtFloat :
|
||||
dprintf( OutputHandle, "%s,\n", Param->Value );
|
||||
break;
|
||||
|
||||
case jtString :
|
||||
dprintf( OutputHandle, "\"%s\",\n", Param->Value );
|
||||
break;
|
||||
|
||||
case jtArray :
|
||||
dprintf( OutputHandle, "[],\n" );
|
||||
break;
|
||||
|
||||
case jtObject :
|
||||
SaveObject( Param, Indent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Shorten spacer
|
||||
SpacerLen -= 2;
|
||||
Spacer[SpacerLen] = 0;
|
||||
Level--;
|
||||
|
||||
// Closing brace
|
||||
if (Level == 0) {
|
||||
dprintf( OutputHandle, "%s}\n", Spacer );
|
||||
}
|
||||
else {
|
||||
dprintf( OutputHandle, "%s},\n", Spacer );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user