Files
redAcore/DataTreeCore.cpp
Charl Wentzel 8f39adb0f3 Important update:
- Buffer:
  - fix undefined value: zero terminate buffer when loading from FD
- DataTree:
  - fix memory leak: destroy temporary search path
  - improve delete(): delete all children for member if no path given
  - bug fix: initiate object with 0 length (not -1)
- JSONparse:
  - Remove level parameter
  - Add path to load/save/print methods (print only portion of tree)
  - fix memory leak: destroy temporary object name
2017-03-29 21:27:28 +02:00

563 lines
15 KiB
C++

/*
* DataTreeCore.cpp
*
* Created on: 5 Mar 2017
* Author: wentzelc
*/
// redA Libraries
#include "DataTreeCore.h"
// Standard C/C++ Libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
//---------------------------------------------------------------------------
CDataTree::CDataTree()
{
// Create Root member of tree
RootMember = CreateMember( NULL );
RootMember->Type = jtObject;
}
//---------------------------------------------------------------------------
CDataTree::~CDataTree()
{
// Destroy Members
DeleteAll();
DestroyMember( &RootMember );
}
//---------------------------------------------------------------------------
TDataMember * CDataTree::CreateMember( const char * Name )
{
TDataMember * Member;
Member = (TDataMember *)calloc( 1, sizeof(TDataMember) );
if (Name && *Name)
{
Member->Name = (char *)malloc( strlen( Name )+1 );
strcpy( Member->Name, Name );
}
return Member;
}
//---------------------------------------------------------------------------
bool CDataTree::AddMember( TDataMember * Parent, TDataMember * Member )
{
TDataMember ** Child;
// Validate
if (!Parent || !Member) {
return false;
}
// Get end of list
Child = &(Parent->FirstChild);
while (*Child) {
Child = &((*Child)->Next);
}
// Add member
*Child = Member;
Parent->Len++;
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::DestroyMember( TDataMember ** Member )
{
TDataMember * NextMember;
// Valdate
if (!Member || !*Member)
return false;
// Get next param in list
NextMember = (*Member)->Next;
// Destroy
if ((*Member)->Name)
free( (*Member)->Name );
if ((*Member)->Value)
free( (*Member)->Value );
while ((*Member)->FirstChild) {
DestroyMember( &((*Member)->FirstChild) );
}
free( *Member );
// Restore list integrity
*Member = NextMember;
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::DestroyValue( TDataMember * Member )
{
// Valdate
if (!Member)
return false;
// Destroy values
if (Member->Value) {
free( Member->Value );
Member->Value = NULL;
}
while (Member->FirstChild) {
DestroyMember( &(Member->FirstChild) );
}
Member->Len = 0;
return true;
}
//---------------------------------------------------------------------------
TDataMember ** CDataTree::GetMemberPtr( TDataMember * BaseMember, const char * Path, bool Create, TDataMember ** Parent )
{
char * WorkPath = NULL;
TDataMember * Member;
TDataMember ** Child = NULL;
char * Pos;
char * EndPos;
char * MemberName;
int Index;
int Count;
bool Last = false;
// Validate
if (!Path || !*Path) {
if (Parent) *Parent = NULL;
return NULL;
}
WorkPath = (char*)malloc( strlen(Path)+1 );
strcpy( WorkPath, Path );
// Set init references
if (Parent) *Parent = NULL;
Child = NULL;
Member = (BaseMember)? BaseMember : RootMember;
// Split path
Pos = (char*)WorkPath;
while (Member && *Pos)
{
// Reset Child reference
Child = NULL;
// Set Name start
MemberName = Pos;
// Find delimiter
if (*Pos == '[')
{
// Validate
if (Create && (Member->Type == jtNull)) {
Member->Type = jtArray; // Convert to array
}
if (Member->Type != jtArray) {
break; // Can't convert something else to an array
}
// Get Index value
Pos++;
while (*Pos && (*Pos != ']')) {
Pos++;
}
if (*Pos != ']') {
break;
}
Index = (int)strtoul( Pos, &EndPos, 10 );
if (EndPos != Pos) {
break;
}
Pos++;
if (!*Pos) {
// Last element
Last = true;
}
// Find next parent
Child = &(Member->FirstChild);
Count = 0;
while (*Child && (Count < Index)) {
Child = &((*Child)->Next);
Count++;
}
if (!*Child && Create) {
if (!Index || (Index = Count + 1)) {
*Child = CreateMember( NULL );
Member->Value++;
}
}
}
else
{
// Validate
if (Create && (Member->Type == jtNull)) {
Member->Type = jtObject; // Convert to object
}
if (Member->Type != jtObject) {
break; // Can't convert something else to an object
}
// Get key value
while (*Pos && (*Pos != '/') && (*Pos != '[')) {
Pos++;
}
if (!*Pos) {
// Last element
Last = true;
}
else {
// Zero terminate name
*Pos = 0;
Pos++;
}
// Find next parent
Child = &(Member->FirstChild);
while (*Child && strcasecmp( (*Child)->Name, MemberName )) {
Child = &((*Child)->Next);
}
if (!*Child && Create) {
*Child = CreateMember( MemberName );
Member->Len++;
}
}
// Set Parent
if (Last && Parent) {
*Parent = Member;
}
// Next level
Member = *Child;
}
// Destroy temp path
free( WorkPath );
return Child;
}
//---------------------------------------------------------------------------
TDataMember * CDataTree::GetMember( TDataMember * BaseMember, const char * Path, bool Create )
{
TDataMember ** Member;
// Get Child
Member = GetMemberPtr( BaseMember, Path, Create );
return *Member;
}
//---------------------------------------------------------------------------
bool CDataTree::Delete( TDataMember * BaseMember, const char * Path )
{
TDataMember * Parent;
TDataMember ** Member;
// Validate
if (!BaseMember && (!Path || !*Path)) {
return false;
}
if (!Path || !*Path) {
// No path - destroy value
DestroyValue( BaseMember );
return true;
}
else if ((Member = GetMemberPtr( BaseMember, Path, false, &Parent ))) {
// If valid path, destroy member
DestroyMember( Member );
Parent->Len--;
return true;
}
else {
return false;
}
}
//---------------------------------------------------------------------------
bool CDataTree::DeleteAll()
{
// Delete all except root member
while (RootMember->FirstChild) {
DestroyMember( &(RootMember->FirstChild) );
}
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetValuePtr( TDataMember * Member, EDataType Type, char * Value, int Len )
{
// Clear previous value
if (Member->Value != NULL) {
DestroyValue( Member );
}
// Set type
Member->Type = Type;
Member->Value = Value;
Member->Len = Len;
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetValue( TDataMember * Member, EDataType Type, const char * Value, int Len )
{
char * NewValue = NULL;
if ((Type == jtString) || (Type == jtFloat) || (Type == jtInt) || (Type == jtBool))
{
// Validate
if (!Value) {
return false;
}
// Check Length
if (Len == -1) {
Len = strlen( Value );
}
// Create copy of value
NewValue = (char *)malloc( sizeof(Len+1) );
if (Value) {
memcpy( NewValue, Value, Len );
} else {
memset( NewValue, 0, Len );
}
NewValue[Len] = 0;
SetValuePtr( Member, Type, NewValue, Len );
}
else {
// Set null value
SetValuePtr( Member, Type, NULL, 0 );
}
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetObject( TDataMember * BaseMember, const char * Path )
{
TDataMember * Member;
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Set as Object
SetValue( Member, jtObject, NULL );
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetArray( TDataMember * BaseMember, const char * Path )
{
TDataMember * Member;
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Set as Object
SetValue( Member, jtArray, NULL );
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetStr( TDataMember * BaseMember, const char * Path, const char * Value, const int Len )
{
TDataMember * Member;
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Create Value
SetValue( Member, jtString, Value, Len );
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetInt( TDataMember * BaseMember, const char * Path, const long Value, const char * Mask )
{
TDataMember * Member;
char ValueStr[20];
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Create Value
sprintf( ValueStr, ((Mask)? Mask : "%ld"), Value );
SetValue( Member, jtInt, ValueStr );
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetFloat( TDataMember * BaseMember, const char * Path, const double Value, const char * Mask )
{
TDataMember * Member;
char ValueStr[20];
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Create Value
sprintf( ValueStr, ((Mask)? Mask : "%lf"), Value );
SetValue( Member, jtFloat, ValueStr );
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetBool( TDataMember * BaseMember, const char * Path, const bool Value )
{
TDataMember * Member;
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Create Value
SetValue( Member, jtBool, ((Value == 0)? "0" : "1") );
return true;
}
//---------------------------------------------------------------------------
bool CDataTree::SetNull( TDataMember * BaseMember, const char * Path )
{
TDataMember * Member;
// Validate
if (!(Member = GetMember( BaseMember, Path, true ))) {
return false;
}
// Create Value
SetValue( Member, jtNull, NULL );
return true;
}
//---------------------------------------------------------------------------
EDataType CDataTree::GetType( TDataMember * BaseMember, const char * Path )
{
TDataMember * Member;
// Validate
if (!(Member = GetMember( BaseMember, Path, false ))) {
return jtNull;
}
return Member->Type;
}
//---------------------------------------------------------------------------
const char * CDataTree::GetStr( TDataMember * BaseMember, const char * Path, const char * Default, bool Create )
{
TDataMember * Member;
// Validate
if ((Member = GetMember( BaseMember, Path, Create )) && (Member->Type == jtString)) {
return Member->Value;
}
else if (Member && Create && (Member->Type == jtNull)) {
SetValue( Member, jtString, Default );
return Default;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------
const char * CDataTree::GetStr( TDataMember * BaseMember, const char * Path, int &Len, const char * Default, bool Create )
{
TDataMember * Member;
// Validate
if ((Member = GetMember( BaseMember, Path, Create )) && (Member->Type == jtString)) {
Len = Member->Len;
return Member->Value;
}
else if (Member && Create && (Member->Type == jtNull)) {
SetValue( Member, jtString, Default );
Len = Member->Len;
return Default;
}
else {
Len = strlen( Default );
return Default;
}
}
//---------------------------------------------------------------------------
const long CDataTree::GetInt( TDataMember * BaseMember, const char * Path, long Default, bool Create, const char * Mask )
{
TDataMember * Member;
// Validate
if ((Member = GetMember( BaseMember, Path, Create )) && (Member->Type == jtInt)) {
return strtol( Member->Value, NULL, 10 );
}
else if (Member && Create && (Member->Type == jtNull)) {
char TempStr[20];
sprintf( TempStr, ((Mask)? Mask : "%ld"), Default );
SetValue( Member, jtInt, TempStr );
return Default;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------
const double CDataTree::GetFloat( TDataMember * BaseMember, const char * Path, double Default, bool Create, const char * Mask )
{
TDataMember * Member;
// Validate
if ((Member = GetMember( BaseMember, Path, Create )) && (Member->Type == jtFloat)) {
return strtod( Member->Value, NULL );
}
else if (Member && Create && (Member->Type == jtNull)) {
char TempStr[20];
sprintf( TempStr, ((Mask)? Mask : "%lf"), Default );
SetValue( Member, jtFloat, TempStr );
return Default;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------
const bool CDataTree::GetBool( TDataMember * BaseMember, const char * Path, bool Default, bool Create )
{
TDataMember * Member;
// Validate
if ((Member = GetMember( BaseMember, Path, Create )) && (Member->Type == jtBool)) {
return ((!strcasecmp( Member->Value, "0" ))? false : true );
}
else if (Member && Create && (Member->Type == jtNull)) {
SetValue( Member, jtBool, ((Default)? "1" : "0") );
return Default;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------