/* * JSONparseCore.cpp * * Created on: 5 Mar 2017 * Author: wentzelc */ // Standard C/C++ Libraries #include #include #include // redA Libraries #include "JSONparseCore.h" //--------------------------------------------------------------------------- CJSONparse::CJSONparse( CDataMember * 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 ) { CDataMember * Member; // 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 (!(Member = DataTree->GetChild( BasePath ))) { Error = true; sprintf( ErrorText, "Invalid root object path" ); OutputHandle = -1; return false; } // Print to file if (!PrintObject( Member, Indent ) || (write( OutputHandle, "\n", 1 ) < 0)) { OutputHandle = -1; return false; } 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::ReadFromString( const char * BasePath, const char * InString, const int Len ) { bool result = false; // Clear Error Error = false; // Load Buffer CreateBuffer( Len ); Buffer->Push( true, InString, Len ); // Continuously refill buffer while loading result = ReadFromBuffer( BasePath ); RefillBuffer = false; // Destroy buffer FreeBuffer(); InputHandle = -1; return result; } //--------------------------------------------------------------------------- bool CJSONparse::ReadFromBuffer( const char * BasePath ) { CDataMember * BaseMember = NULL; // Validate if (!DataTree || !Buffer) { return false; } // Clear Error Error = false; // Get/Create Root object if (!(BaseMember = DataTree->GetChild( BasePath, true ))) { Error = true; sprintf( ErrorText, "Invalid root object path" ); return false; } // Delete existing object contents BaseMember->Clear(); // 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( CDataMember * Object ) { CDataMember * Member = NULL; char * MemberName = NULL; int Len = 0; // Check for start of Object if (*BufPos != '{') { return false; } BufPos++; // Set Type Object->SetValue( 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 = Object->GetChild( 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 Object->Delete( 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( CDataMember * Array ) { CDataMember ** Member; // Check for start of Object if (*BufPos != '[') { return false; } BufPos++; // Set Type Array->SetValue( jtArray ); Member = &(Array->FirstChild); while (true) { // Look for Member Name SkipWhiteSpace(); if (*BufPos == ']') { break; } // Add new element *Member = new CDataMember( Array ); // Get Value SkipWhiteSpace(); if (!ParseObject( *Member ) && !Error && !ParseArray( *Member ) && !Error && !ParseString( *Member ) && !Error && !ParsePrimitive( *Member ) ) {} if (Error) { delete Member; return false; } else { Member = &((*Member)->NextPeer); } // 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( CDataMember * Member ) { char * Value = NULL; int Len = 0; // Try to parse if (!ParseString( &Value, Len )) { return false; } // Set string Member->SetValuePtr( jtString, Value, Len ); return true; } //--------------------------------------------------------------------------- bool CJSONparse::ParsePrimitive( CDataMember * 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 )) { Member->SetValue( jtNull ); } else if ((Len == 4) && !strncasecmp( Mark, "true", 4 )) { Member->SetValue( jtBool, "1" ); } else if ((Len == 5) && !strncasecmp( Mark, "false", 5 )) { Member->SetValue( 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; Member->SetValuePtr( 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; Member->SetValuePtr( 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 ) { int BytesWritten = 0; // Start quote if (write( OutputHandle, "\"", 1 ) < 0) return false; // Content BufPos = String; while (true) { // Scan for special chars Mark = BufPos; while ((*BufPos >= 32) && (*BufPos <= 126) && (*BufPos != '\\') && (*BufPos != '/') && (*BufPos != '"')) BufPos++; // Print Portion if (write( OutputHandle, Mark, (BufPos-Mark) ) < 0) return false; // Handle special chars if (BufPos-String >= Len) { break; } else { switch (*BufPos) { case '\b': BytesWritten = write( OutputHandle, "\\b", 2 ); break; case '\f': BytesWritten = write( OutputHandle, "\\f", 2 ); break; case '\n': BytesWritten = write( OutputHandle, "\\n", 2 ); break; case '\r': BytesWritten = write( OutputHandle, "\\r", 2 ); break; case '\t': BytesWritten = write( OutputHandle, "\\t", 2 ); break; case '/': BytesWritten = write( OutputHandle, "\\/", 2 ); break; case '\\': BytesWritten = write( OutputHandle, "\\\\", 2 ); break; case '"': BytesWritten = write( OutputHandle, "\\\"", 2 ); break; default: BytesWritten = dprintf( OutputHandle, "\\u%04X", (unsigned char)*BufPos ); break; } if (BytesWritten < 0) return false; BufPos++; } } // End Quote if (write( OutputHandle, "\"", 1 ) < 0) return false; return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintObject( CDataMember * Object, const int Indent ) { CDataMember * Member; bool First = true; bool Last = false; int Count = 0; // Opening brace if (write( OutputHandle, "{", 1 ) < 0) return false; // Check if empty if (Object->Len == 0) { if (write( OutputHandle, "}", 1 ) < 0) return false; return true; } // Extend spacer if (Indent) { memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; } // Save parameters for (Member = Object->FirstChild; Member != NULL; (Member = Member->NextPeer)) { // Whitespace around first bracket if (Indent) { if (First) { First = false; if (write( OutputHandle, "\n", 1 ) < 0) return false; } if (write( OutputHandle, Spacer, SpacerLen ) < 0) return false; } // Print key name if (!PrintString( Member->Name, strlen(Member->Name) )) return false; if (Indent) { if (write( OutputHandle," : ", 3 ) < 0) return false; } else { if (write( OutputHandle, ":", 1 ) < 0) return false; } // Print value Last = (++Count >= Object->Len); switch (Member->Type) { case jtNull : if (write( OutputHandle, "null", 4 ) < 0) return false; break; case jtBool : if (!strcmp( Member->Value, "0" )) { if (write( OutputHandle, "false", 5 ) < 0) return false; } else { if (write( OutputHandle, "true", 4 ) < 0) return false; } break; case jtInt : case jtFloat : if (write( OutputHandle, Member->Value, Member->Len ) < 0) return false; break; case jtString : if (!PrintString( Member->Value, Member->Len )) return false; break; case jtArray : if (!PrintArray( Member, Indent )) return false; break; case jtObject : if (!PrintObject( Member, Indent )) return false; break; } if (!Last) { if (write( OutputHandle, ",", 1 ) < 0) return false; } if (Indent) { if (write( OutputHandle, "\n", 1 ) < 0) return false; } } // Shorten spacer if (Indent) { SpacerLen -= 2; Spacer[SpacerLen] = 0; } // Closing brace if (Indent) { if (write( OutputHandle, Spacer, SpacerLen ) < 0) return false; } if (write( OutputHandle, "}", 1 ) < 0) return false; return true; } //--------------------------------------------------------------------------- bool CJSONparse::PrintArray( CDataMember * Array, const int Indent ) { CDataMember * Member; bool First = true; bool Last = false; int Count = 0; // Opening brace if (write( OutputHandle, "[", 1 ) < 0) return false; // Check if empty if (Array->Len == 0) { if (write( OutputHandle, "]", 1 ) < 0) return false; return true; } // Extend spacer if (Indent) { memset( &Spacer[SpacerLen], ' ', 2 ); SpacerLen += 2; Spacer[SpacerLen] = 0; } // Save parameters for (Member = Array->FirstChild; Member != NULL; (Member = Member->NextPeer)) { // Whitespace around brace if (Indent) { if (First) { First = false; if (write( OutputHandle, "\n", 1 ) < 0) return false; } if (write( OutputHandle, Spacer, SpacerLen ) < 0) return false; } Last = (++Count >= Array->Len); switch (Member->Type) { case jtNull : if (write( OutputHandle, "null", 4 ) < 0) return false; break; case jtBool : case jtInt : case jtFloat : if (write( OutputHandle, Member->Value, Member->Len ) < 0) return false; break; case jtString : if ((write( OutputHandle, "\"", 1 ) < 0) || (write( OutputHandle, Member->Value, Member->Len ) < 0) || (write( OutputHandle, "\"", 1 ) < 0)) return false; break; case jtArray : if (!PrintArray( Member, Indent )) return false; break; case jtObject : if (!PrintObject( Member, Indent )) return false; break; } if (!Last) { if (write( OutputHandle, ",", 1 ) < 0) return false; } if (Indent) { if (write( OutputHandle, "\n", 1 ) < 0) return false; } } // Shorten spacer if (Indent) { SpacerLen -= 2; Spacer[SpacerLen] = 0; } // Closing brace if (Indent) { if (write( OutputHandle, Spacer, SpacerLen ) < 0) return false; } if (write( OutputHandle, "]", 1 ) < 0) return false; return true; } //---------------------------------------------------------------------------