- Library Clean up: - Removed all unused C/C++ libraries from source - First C/C++ libraries then redA libraries - Library changes: - renamed BufferCore -> CharBufferCore - added ItemBufferCore - CharBufferCore: - Derive RollingBuffer & ShiftBuffer from common class CharBuffer - CharBuffer is mostly a virtual class (interface)
615 lines
16 KiB
C++
615 lines
16 KiB
C++
/*
|
|
* DataTreeCore.cpp
|
|
*
|
|
* Created on: 5 Mar 2017
|
|
* Author: wentzelc
|
|
*/
|
|
|
|
// Standard C/C++ Libraries
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
// redA Libraries
|
|
#include "DataTreeCore.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, const int Len )
|
|
{
|
|
TDataMember * Member;
|
|
|
|
// Create data structure
|
|
Member = (TDataMember *)calloc( 1, sizeof(TDataMember) );
|
|
|
|
// Set name
|
|
if (Name)
|
|
{
|
|
Member->NameLen = (Len == -1)? strlen( Name ) : Len ;
|
|
Member->Name = (char *)malloc( Member->NameLen+1 );
|
|
memcpy( Member->Name, Name, Member->NameLen );
|
|
Member->Name[ Member->NameLen ] = 0;
|
|
}
|
|
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 )
|
|
{
|
|
TDataMember * Member;
|
|
TDataMember ** Child = NULL;
|
|
char * Pos;
|
|
char * EndPos;
|
|
char * Key;
|
|
unsigned short KeyLen;
|
|
int Index;
|
|
int Count;
|
|
bool Last = false;
|
|
|
|
// Validate
|
|
if (!Path || !*Path) {
|
|
if (Parent) *Parent = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
// Set init references
|
|
if (Parent) *Parent = NULL;
|
|
Child = NULL;
|
|
Member = (BaseMember)? BaseMember : RootMember;
|
|
|
|
// Split path
|
|
Pos = (char*)Path;
|
|
while (Member && *Pos)
|
|
{
|
|
// Reset Child reference
|
|
Child = NULL;
|
|
|
|
// 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
|
|
}
|
|
|
|
// Set Index start
|
|
Pos++;
|
|
Key = Pos;
|
|
|
|
// Get Index value
|
|
while (*Pos && (*Pos != ']'))
|
|
Pos++;
|
|
if (!*Pos)
|
|
break;
|
|
|
|
// Check if last
|
|
Pos++;
|
|
if (!*Pos)
|
|
Last = true;
|
|
|
|
if (Pos == Key+1) {
|
|
// Empty bracket only allowed for create
|
|
if (!Create)
|
|
break;
|
|
Index = -1;
|
|
|
|
// find end of list
|
|
Child = &(Member->FirstChild);
|
|
while (*Child)
|
|
Child = &((*Child)->Next);
|
|
}
|
|
else {
|
|
// Get requested index
|
|
Index = (int)strtoul( Key, &EndPos, 10 );
|
|
if (EndPos != Pos-1)
|
|
break;
|
|
|
|
// Find element at requested index
|
|
Child = &(Member->FirstChild);
|
|
Count = 0;
|
|
while (*Child && (Count < Index)) {
|
|
Child = &((*Child)->Next);
|
|
Count++;
|
|
}
|
|
}
|
|
|
|
// Create element if needed
|
|
if (!*Child && Create) {
|
|
if ((Index == -1) || (Index = Count + 1)) {
|
|
*Child = CreateMember( NULL );
|
|
Member->Len++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Skip separator
|
|
if (*Pos == '/') {
|
|
Pos++;
|
|
}
|
|
|
|
// 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
|
|
Key = Pos;
|
|
KeyLen = 0;
|
|
while (*Pos && (*Pos != '/') && (*Pos != '[')) {
|
|
KeyLen++;
|
|
Pos++;
|
|
}
|
|
|
|
// More elements?
|
|
if (!*Pos) {
|
|
Last = true;
|
|
}
|
|
|
|
// Find next parent
|
|
Child = &(Member->FirstChild);
|
|
while (*Child && (((*Child)->NameLen != KeyLen) || strncasecmp( (*Child)->Name, Key, KeyLen ))) {
|
|
Child = &((*Child)->Next);
|
|
}
|
|
if (!*Child && Create) {
|
|
*Child = CreateMember( Key, KeyLen );
|
|
Member->Len++;
|
|
}
|
|
}
|
|
|
|
// Set Parent
|
|
if (Last && Parent) {
|
|
*Parent = Member;
|
|
}
|
|
|
|
// Next level
|
|
Member = *Child;
|
|
}
|
|
return Child;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TDataMember * CDataTree::GetMember( TDataMember * BaseMember, const char * Path, bool Create )
|
|
{
|
|
TDataMember ** Member;
|
|
|
|
// Get Child
|
|
Member = GetMemberPtr( BaseMember, Path, Create );
|
|
return ((Member)? *Member : NULL);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TDataMember * CDataTree::GetIndexChild( TDataMember * Parent, const int Index )
|
|
{
|
|
TDataMember * Child;
|
|
int Count;
|
|
|
|
// Get Parent
|
|
if (!Parent)
|
|
return NULL;
|
|
|
|
// Get Indexed child
|
|
Count = 0;
|
|
Child = Parent->FirstChild;
|
|
while (Child && (Count < Index)) {
|
|
Child = Child->Next;
|
|
Count++;
|
|
}
|
|
|
|
// Return child
|
|
return Child;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
TDataMember * CDataTree::GetFirstChild( TDataMember * BaseMember, const char * Path, bool Create )
|
|
{
|
|
TDataMember * Member;
|
|
|
|
// Validate
|
|
if (!(Member = GetMember( BaseMember, Path, Create ))) {
|
|
return NULL;
|
|
}
|
|
|
|
// Return child
|
|
return Member->FirstChild;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
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 || (Member->FirstChild != 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( 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) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
|
|
return Member->Value;
|
|
}
|
|
else if (Member && Create && (Member->Type == jtNull)) {
|
|
SetValue( Member, jtString, Default );
|
|
return Member->Value;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
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) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
|
|
Len = Member->Len;
|
|
return Member->Value;
|
|
}
|
|
else if (Member && Create && (Member->Type == jtNull)) {
|
|
SetValue( Member, jtString, Default );
|
|
Len = Member->Len;
|
|
return Member->Value;
|
|
}
|
|
else {
|
|
Len = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
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;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|