/* * JSONparseCore.cpp * * Created on: 5 Mar 2017 * Author: wentzelc */ // Standard C/C++ Libraries #include #include #include // redA Libraries #include "JSONparseCore.h" //--------------------------------------------------------------------------- CJSONparse::CJSONparse( CDataTree * pDataTree ) { // Object tree DataTree = pDataTree; // File Operation InputHandle = -1; OutputHandle = -1; Buffer = NULL; // Parsing operation BufPos = NULL; Mark = NULL; LineNo = 0; CharNo = 0; RefillBuffer = false; // Printing operation Spacer[0] = 0; SpacerLen = 0; // Error reporting Error = false; ErrorText[0] = 0; } //--------------------------------------------------------------------------- CJSONparse::~CJSONparse() { // Destroy buffer if (Buffer) { delete Buffer; } } //--------------------------------------------------------------------------- bool CJSONparse::WriteToScreen( const char * BasePath, const int Indent ) { // Print to screen WriteToHandle( BasePath, 1, Indent ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::WriteToFile( const char * BasePath, const char * Path, const char * FileName, const int Indent ) { char FilePath[250] = ""; int PathLen = 0; // Validate if (!FileName) { Error = true; sprintf( ErrorText, "No File name specified" ); return false; } // Build file name if (Path && *Path) { strcpy( FilePath, Path ); PathLen = strlen( FilePath ); if (FilePath[PathLen] != '/') { FilePath[PathLen++] = '/'; } } strcpy( &FilePath[PathLen], FileName ); // Read file return WriteToFile( BasePath, FilePath, Indent ); } //--------------------------------------------------------------------------- bool CJSONparse::WriteToFile( const char * BasePath, const char * FilePath, const int Indent ) { int Handle = -1; // Clear Error Error = false; // Validate if (!FilePath || !FilePath[0]) { Error = true; sprintf( ErrorText, "No File path specified" ); return false; } // Open file if ((Handle = open( FilePath, O_CREAT|O_WRONLY|O_TRUNC, 660 )) < 0) { Error = true; sprintf( ErrorText, "Could not open file" ); return false; } // Save to file WriteToHandle( BasePath, Handle, Indent ); // Close file close( Handle ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::WriteToHandle( const char * BasePath, const int Handle, const int Indent ) { TDataMember * BaseMember; // Validate if (!DataTree) { return false; } // Open file if (Handle < 0) { Error = true; sprintf( ErrorText, "Could not write to invalid handle" ); return false; } OutputHandle = Handle; // Get Root object if (!BasePath || !*BasePath) { BaseMember = DataTree->GetRootMember(); } else if (!(BaseMember = DataTree->GetMember( NULL, BasePath ))) { Error = true; sprintf( ErrorText, "Invalid root object path" ); return false; } // Print to file PrintObject( BaseMember, Indent ); write( OutputHandle, "\n", 1 ); OutputHandle = -1; return true; } //--------------------------------------------------------------------------- bool CJSONparse::ReadFromFile( const char * BasePath, const char * Path, const char * FileName ) { char FilePath[250] = ""; int PathLen = 0; // Validate if (!FileName) { Error = true; sprintf( ErrorText, "No File name specified" ); return false; } // Build file name if (Path && *Path) { strcpy( FilePath, Path ); PathLen = strlen( FilePath ); if (FilePath[PathLen] != '/') { FilePath[PathLen++] = '/'; } } strcpy( &FilePath[PathLen], FileName ); // Read file return ReadFromFile( BasePath, FilePath ); } //--------------------------------------------------------------------------- bool CJSONparse::ReadFromFile( const char * BasePath, const char * FilePath ) { int Handle = -1; bool result = false; // Validate if (!DataTree) { return false; } // Clear Error Error = false; // Validate if (!FilePath || !FilePath[0]) { Error = true; sprintf( ErrorText, "No File path specified" ); return false; } // Open file if ((Handle = open( FilePath, O_RDONLY )) < 0) { Error = true; sprintf( ErrorText, "Could not open file" ); return false; } // Continuously refill buffer while loading result = ReadFromHandle( BasePath, Handle, true ); // Close File close( Handle ); return result; } //--------------------------------------------------------------------------- bool CJSONparse::ReadFromHandle( const char * BasePath, int Handle, bool pRefillBuffer ) { bool result = false; // Clear Error Error = false; // Validate if (Handle < 0) { Error = true; sprintf( ErrorText, "Cannot read from invalid handle" ); return false; } InputHandle = Handle; // Load Buffer CreateBuffer( 50 ); if (!FillBuffer()) { Error = true; sprintf( ErrorText, "Could not read from file" ); FreeBuffer(); return false; } // Continuously refill buffer while loading RefillBuffer = pRefillBuffer; result = ReadFromBuffer( BasePath ); RefillBuffer = false; // Destroy buffer FreeBuffer(); InputHandle = -1; return result; } //--------------------------------------------------------------------------- bool CJSONparse::ReadFromBuffer( const char * BasePath ) { TDataMember * BaseMember = NULL; // Validate if (!DataTree || !Buffer) { return false; } // Clear Error Error = false; // Get/Create Root object if (!BasePath || !*BasePath) { BaseMember = DataTree->GetRootMember(); } else if (!(BaseMember = DataTree->GetMember( NULL, BasePath, true ))) { Error = true; sprintf( ErrorText, "Invalid root object path" ); return false; } // Delete existing object contents DataTree->Delete( BaseMember, NULL ); // Position Counters LineNo = 1; CharNo = 0; // Parse Root Object SkipWhiteSpace(); if (!ParseObject( BaseMember )) { if (!Error) { Error = true; CharNo += BufPos-Mark; sprintf( ErrorText, "First entry in file must be an Object on line %d:%d", LineNo, CharNo ); } FreeBuffer(); return false; } // Ensure remainder of file is empty SkipWhiteSpace(); if (*BufPos != 0) { Error = true; CharNo += BufPos-Mark; sprintf( ErrorText, "No content expected after Root object on line %d:%d", LineNo, CharNo ); FreeBuffer(); return false; } // Success return true; } //--------------------------------------------------------------------------- bool CJSONparse::CreateBuffer( int pBufLen ) { // Validate if (!pBufLen) return false; // Create buffer Buffer = new CShiftBuffer( pBufLen ); // Reset markers Buffer->PeekDirect( &BufPos, 0 ); Mark = BufPos; return true; } //--------------------------------------------------------------------------- bool CJSONparse::FillBuffer() { char * BufStart; int PosShift = BufPos - Mark; // Remove all bytes up to Mark Buffer->PeekDirect( &BufStart, 0 ); Buffer->Clear( Mark-BufStart ); // Read from file Buffer->ReadFromFD( InputHandle ); // Reset Mark to start of buffer Mark = BufStart; BufPos = Mark + PosShift; return true; } //--------------------------------------------------------------------------- void CJSONparse::FreeBuffer() { // Destroy buffer if (Buffer) { delete Buffer; Buffer = NULL; // Update Markers BufPos = NULL; Mark = NULL; } } //--------------------------------------------------------------------------- void CJSONparse::SkipWhiteSpace() { while (true) { // Append buffer if required if (!*BufPos && RefillBuffer) FillBuffer(); // Skip whitespace if (!isspace(*BufPos)) { CharNo += BufPos-Mark; Mark = BufPos; break; } if (*BufPos == '\n') { Mark = BufPos; LineNo++; CharNo = 0; } BufPos++; } } //--------------------------------------------------------------------------- bool CJSONparse::ParseString( char ** Value, int &Len ) { char * EndMark; char * ValuePos = NULL; char HexVal[5] = ""; // Check for opening quote if (*BufPos != '"') { return false; } // Clear values *Value = NULL; Len = 0; // Mark start of quote BufPos++; // Check for closing quote while (true) { // Check for special characters if ((EndMark = strpbrk( BufPos, "\"/\\\n\t\b\f\n\r" ))) { BufPos = EndMark; } else if (RefillBuffer) { FillBuffer(); continue; } if (*BufPos == '"') { // End of string found BufPos++; break; } else if (!*BufPos) { Error = true; sprintf( ErrorText, "Expect closing '\"' for string on line %d:%d", LineNo, CharNo+1 ); return false; } else if (*BufPos == '\\') { if (!*(BufPos+1) && RefillBuffer) { if (FillBuffer()) { continue; } } if (*(BufPos+1) == 'u') { for (EndMark = BufPos+2; EndMark < BufPos+6; EndMark++) { if (!*EndMark && RefillBuffer) { FillBuffer(); continue; } if (!isxdigit( *EndMark )) { Error = true; CharNo += BufPos-EndMark; sprintf( ErrorText, "Expect 4-digit hex value for escape value on line %d:%d", LineNo, CharNo ); return false; } } BufPos += 6; Len -= 5; } else if (strchr( "bfnrt/\\\"", *(BufPos+1) )) { BufPos += 2; Len -= 1; } else { Error = true; CharNo += BufPos-Mark; sprintf( ErrorText, "Invalid escape sequence on line %d:%d", LineNo, CharNo ); return false; } } else { Error = true; CharNo += BufPos-Mark; sprintf( ErrorText, "Un-escaped special character in string on line %d:%d", LineNo, CharNo ); return false; } } // Create Return value pointer Len += BufPos-Mark-2; *Value = (char*)malloc( Len+1 ); ValuePos = *Value; // Convert value BufPos = Mark+1; while ((EndMark = strpbrk( BufPos, "\"\\" ))) { // Copy portion memcpy( ValuePos, BufPos, (EndMark-BufPos) ); ValuePos += (EndMark-BufPos); BufPos = EndMark; if (*BufPos == '"') { break; } else if (*BufPos== '\\') { if (*(BufPos+1) == 'u') { strncpy( HexVal, BufPos+2, 4 ); *ValuePos = (char)strtol( HexVal, NULL, 16 ); ValuePos++; BufPos += 6; } else { switch (*(BufPos+1)) { case 'b': *ValuePos = '\b'; break; case 'f': *ValuePos = '\f'; break; case 'n': *ValuePos = '\n'; break; case 'r': *ValuePos = '\r'; break; case 't': *ValuePos = '\t'; break; case '/': *ValuePos = '/'; break; case '\\': *ValuePos = '\\'; break; case '"': *ValuePos = '"'; break; } ValuePos++; BufPos +=2; } } } *ValuePos = 0; BufPos++; // Success return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParseObject( TDataMember * Object ) { TDataMember * Member = NULL; char * MemberName = NULL; int Len = 0; // Check for start of Object if (*BufPos != '{') { return false; } BufPos++; // Set Type DataTree->SetValue( Object, jtObject ); while (true) { // Evaluate key name SkipWhiteSpace(); if (*BufPos == '}') { // End of Object break; } else if (!ParseString( &MemberName, Len )) { if (!Error) { Error = true; sprintf( ErrorText, "Expect quoted key name on line %d:%d", LineNo, CharNo ); } return false; } else if (!Len) { Error = true; sprintf( ErrorText, "Empty parameter name on line %d:%d", LineNo, CharNo ); return false; } // Check if Member exists Member = DataTree->GetMember( Object, MemberName, true ); // Check for delimiter SkipWhiteSpace(); if (*BufPos != ':') { Error = true; sprintf( ErrorText, "Expected ':' delimiter on line %d:%d", LineNo, CharNo ); return false; } BufPos++; // Get Value SkipWhiteSpace(); if (!ParseObject( Member ) && !Error && !ParseArray( Member ) && !Error && !ParseString( Member ) && !Error && !ParsePrimitive( Member ) ) {} if (Error) { // Destroy member DataTree->Delete( Object, MemberName ); return false; } // Free Name free( MemberName ); // 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:%d", LineNo, CharNo ); return false; } BufPos++; // success return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParseArray( TDataMember * Array ) { TDataMember ** Member; TDataMember * PrevMember; // Check for start of Object if (*BufPos != '[') { return false; } BufPos++; // Set Type DataTree->SetValue( Array, jtArray ); Member = &(Array->FirstChild); PrevMember = NULL; while (true) { // Look for Member Name SkipWhiteSpace(); if (*BufPos == ']') { break; } // Add new element *Member = DataTree->CreateMember( Array, PrevMember, NULL ); // Get Value SkipWhiteSpace(); if (!ParseObject( *Member ) && !Error && !ParseArray( *Member ) && !Error && !ParseString( *Member ) && !Error && !ParsePrimitive( *Member ) ) {} if (Error) { DataTree->DestroyMember( Member ); return false; } else { PrevMember = *Member; Member = &((*Member)->Next); } // 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 array ']' expected on line %d:%d", LineNo, CharNo ); return false; } BufPos++; // success return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParseString( TDataMember * Member ) { char * Value = NULL; int Len = 0; // Try to parse if (!ParseString( &Value, Len )) { return false; } // Set string DataTree->SetValuePtr( Member, jtString, Value, Len ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParsePrimitive( TDataMember * Member ) { char * Value = NULL; int Len = 0; char * EndMark; // Get end of value while (true) { if (!*BufPos && RefillBuffer) FillBuffer(); if (!*BufPos || isspace(*BufPos) || (*BufPos == ',') || (*BufPos == '}') || (*BufPos == ']')) break; BufPos++; } // Check length of value Len = BufPos - Mark; if (!Len) { Error = true; sprintf( ErrorText, "Missing param value on line %d:%d", LineNo, CharNo ); return false; } // Check for primitive values if ((Len == 4) && !strncasecmp( Mark, "null", 4 )) { DataTree->SetValuePtr( Member, jtNull, NULL, -1 ); } else if ((Len == 4) && !strncasecmp( Mark, "true", 4 )) { DataTree->SetValue( Member, jtBool, "1" ); } else if ((Len == 5) && !strncasecmp( Mark, "false", 5 )) { DataTree->SetValue( Member, jtBool, "0" ); } else { // Try conversion to int strtol( Mark, &EndMark, 10 ); if (EndMark == BufPos) { Value = (char*)malloc( Len+1 ); memcpy( Value, Mark, Len ); Value[Len] = 0; DataTree->SetValuePtr( Member, jtInt, Value, Len ); } else { // Try conversion to float strtod( Mark, &EndMark ); if (EndMark == BufPos) { Value = (char*)malloc( Len+1 ); memcpy( Value, Mark, Len ); Value[Len] = 0; DataTree->SetValuePtr( Member, jtFloat, Value, Len ); } else { Error = true; sprintf( ErrorText, "Invalid primitive param value on line %d:%d", LineNo, CharNo ); return false; } } } // Success return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintString( char * String, int Len ) { // Start quote write( OutputHandle, "\"", 1 ); // Content BufPos = String; while (true) { // Scan for special chars Mark = BufPos; while ((*BufPos >= 32) && (*BufPos <= 126) && (*BufPos != '\\') && (*BufPos != '/') && (*BufPos != '"')) BufPos++; // Print Portion write( OutputHandle, Mark, (BufPos-Mark) ); // Handle special chars if (!*BufPos) { break; } else { switch (*BufPos) { case '\b': write( OutputHandle, "\\b", 2 ); break; case '\f': write( OutputHandle, "\\f", 2 ); break; case '\n': write( OutputHandle, "\\n", 2 ); break; case '\r': write( OutputHandle, "\\r", 2 ); break; case '\t': write( OutputHandle, "\\t", 2 ); break; case '/': write( OutputHandle, "\\/", 2 ); break; case '\\': write( OutputHandle, "\\\\", 2 ); break; case '"': write( OutputHandle, "\\\"", 2 ); break; default: dprintf( OutputHandle, "\\u%04X", (unsigned char)*BufPos ); break; } BufPos++; } } // End Quote write( OutputHandle, "\"", 1 ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintObject( TDataMember * Object, const int Indent ) { TDataMember * Member; bool First = true; bool Last = false; int Count = 0; // Opening brace write( OutputHandle, "{", 1 ); // Extend spacer if (Indent) { memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; } // Save parameters for (Member = Object->FirstChild; Member != NULL; (Member = Member->Next)) { // Whitespace around first bracket if (Indent) { if (First) { First = false; write( OutputHandle, "\n", 1 ); } write( OutputHandle, Spacer, SpacerLen ); } // Print key name PrintString( Member->Name, strlen(Member->Name) ); if (Indent) { write( OutputHandle," : ", 3 ); } else { write( OutputHandle, ":", 1 ); } // Print value Last = (++Count >= Object->Len); switch (Member->Type) { case jtNull : write( OutputHandle, "null", 4 ); break; case jtBool : if (!strcmp( Member->Value, "0" )) write( OutputHandle, "false", 5 ); else write( OutputHandle, "true", 4 ); break; case jtInt : case jtFloat : write( OutputHandle, Member->Value, Member->Len ); break; case jtString : PrintString( Member->Value, Member->Len ); break; case jtArray : PrintArray( Member, Indent ); break; case jtObject : PrintObject( Member, Indent ); break; } if (!Last) write( OutputHandle, ",", 1 ); if (Indent) write( OutputHandle, "\n", 1 ); } // Shorten spacer if (Indent) { SpacerLen -= 2; Spacer[SpacerLen] = 0; } // Closing brace if (Indent) write( OutputHandle, Spacer, SpacerLen ); write( OutputHandle, "}", 1 ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintArray( TDataMember * Array, const int Indent ) { TDataMember * Member; bool First = true; bool Last = false; int Count = 0; // Opening brace write( OutputHandle, "[", 1 ); // Extend spacer if (Indent) { memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; } // Save parameters for (Member = Array->FirstChild; Member != NULL; (Member = Member->Next)) { // Whitespace around brace if (Indent) { if (First) { First = false; write( OutputHandle, "\n", 1 ); } write( OutputHandle, Spacer, SpacerLen ); } Last = (++Count >= Array->Len); switch (Member->Type) { case jtNull : write( OutputHandle, "null", 4 ); break; case jtBool : case jtInt : case jtFloat : write( OutputHandle, Member->Value, Member->Len ); break; case jtString : write( OutputHandle, "\"", 1 ); write( OutputHandle, Member->Value, Member->Len ); write( OutputHandle, "\"", 1 ); break; case jtArray : PrintArray( Member, Indent ); break; case jtObject : PrintObject( Member, Indent ); break; } if (!Last) write( OutputHandle, ",", 1 ); if (Indent) write( OutputHandle, "\n", 1 ); } // Shorten spacer if (Indent) { SpacerLen -= 2; Spacer[SpacerLen] = 0; } // Closing brace if (Indent) write( OutputHandle, Spacer, SpacerLen ); write( OutputHandle, "]", 1 ); return true; } //---------------------------------------------------------------------------