Files
redAcore/DataTreeCore.cpp
Charl Wentzel 429dae5f50 Minor update
- DataTreeCore:
  - Use strdup() to copy strings
2019-07-29 18:48:46 +02:00

649 lines
17 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"
//---------------------------------------------------------------------------
CDataMember::CDataMember( const char * pName, const int pLen )
{
if (pName) {
Name = strdup( pName );
NameLen = (pLen == -1)? strlen( pName ) : pLen ;
}
else {
Name = NULL;
NameLen = 0;
}
Type = jtNull;
Value = NULL;
Len = 0;
FirstChild = NULL;
LastChild = NULL;
Parent = NULL;
PrevPeer = NULL;
NextPeer = NULL;
}
//---------------------------------------------------------------------------
CDataMember::CDataMember( CDataMember * pParent, const char * pName, const int pLen )
{
if (pName) {
Name = strdup( pName );
NameLen = (pLen == -1)? strlen( pName ) : pLen ;
}
else {
Name = NULL;
NameLen = 0;
}
Type = jtNull;
Value = NULL;
Len = 0;
FirstChild = NULL;
LastChild = NULL;
if (!pParent) {
Parent = NULL;
PrevPeer = NULL;
NextPeer = NULL;
}
else {
// Clear/reset parent if not object
Parent = pParent;
if ((Parent->Type != jtNull) && (Parent->Type != jtObject) && (Parent->Type != jtArray)) {
Parent->Clear();
}
// Insert into Parent & Peer lists
Parent->Len++;
if (!Parent->FirstChild) {
PrevPeer = NULL;
NextPeer = NULL;
Parent->FirstChild = this;
Parent->LastChild = this;
}
else {
PrevPeer = Parent->LastChild;
Parent->LastChild->NextPeer = this;
NextPeer = NULL;
Parent->LastChild = this;
}
}
}
//---------------------------------------------------------------------------
CDataMember::~CDataMember()
{
// Remove from parent
if (Parent) {
Parent->Len--;
if (this == Parent->LastChild) {
Parent->LastChild = PrevPeer;
}
if (this == Parent->FirstChild) {
Parent->FirstChild = NextPeer;
}
}
if (PrevPeer) {
PrevPeer->NextPeer = NextPeer;
}
if (NextPeer) {
NextPeer->PrevPeer = PrevPeer;
}
// Destroy value/children
Clear();
// Destroy member
if (Name)
free( Name );
}
//---------------------------------------------------------------------------
CDataMember * CDataMember::CreateChild( const char * Name, const int Len )
{
CDataMember * Member;
Member = new CDataMember( this, Name, Len );
return Member;
}
//---------------------------------------------------------------------------
bool CDataMember::Clear()
{
// Clear value
if (Value) {
free( Value );
Value = NULL;
}
// Clear children
while (FirstChild) {
delete FirstChild;
}
// Reset value
Type = jtNull;
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::ClearCh( const char * Path )
{
CDataMember * Child = NULL;
// Find member
if (!(Child = (!Path || !*Path)? this : GetChild( Path, false )))
return false;
// Clear
Child->Clear();
return true;
}
//---------------------------------------------------------------------------
CDataMember * CDataMember::GetChild( const char * Path, bool Create )
{
CDataMember * Member;
CDataMember ** Child;
char * Pos;
char * EndPos;
char * Key;
unsigned short KeyLen;
int Index;
int Count;
// Validate
if (!Path || !*Path) {
return this;
}
// Set init references
Child = NULL;
Member = this;
// 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;
Pos++;
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)->NextPeer);
}
}
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)->NextPeer);
Count++;
}
}
// Create element if needed
if (!*Child && Create) {
if ((Index == -1) || (Index == Count)) {
*Child = new CDataMember( Member, NULL );
}
}
}
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++;
}
// Find next parent
Child = &(Member->FirstChild);
while (*Child && (((*Child)->NameLen != KeyLen) || strncasecmp( (*Child)->Name, Key, KeyLen ))) {
Child = &((*Child)->NextPeer);
}
if (!*Child && Create) {
*Child = new CDataMember( Member, Key, KeyLen );
}
}
// Next level
Member = *Child;
}
return (Child)? *Child : NULL;
}
//---------------------------------------------------------------------------
CDataMember * CDataMember::GetChFirstChild( const char * Path, bool Create )
{
CDataMember * Member = NULL;
// Find member
Member = (!Path || !*Path)? this : GetChild( Path, Create );
// Check if valid type
if (!Member || ((Member->Type != jtObject) && (Member->Type != jtNull))) {
return NULL;
}
else if (Create && (Member->Type == jtNull)) {
Member->SetObject(); // Set newly created member to object
}
return Member->FirstChild;
}
//---------------------------------------------------------------------------
CDataMember * CDataMember::GetChElement( const char * Path, const int Index, bool Create )
{
CDataMember * Member = NULL;
CDataMember * Child = NULL;
int Count;
// Find Member
Member = (!Path || !*Path)? this : GetChild( Path, Create );
// Check if valid type
if (!Member || ((Member->Type != jtArray) && (Member->Type != jtNull))) {
return NULL;
}
else if (Create && (Member->Type == jtNull)) {
Member->SetArray(); // Set newly created member to array
}
// Find element at position
Count = 0;
Child = Member->FirstChild;
while (Child && (Count < Index)) {
Child = Child->NextPeer;
Count++;
}
// Return child
return Child;
}
//---------------------------------------------------------------------------
bool CDataMember::DeleteCh( const char * Path )
{
CDataMember * Member;
if (!Path || !*Path) {
// No path - destroy value
Clear();
return true;
}
else if ((Member = GetChild( Path, false ))) {
// If valid path, destroy member
delete Member;
return true;
}
else {
return false;
}
}
//---------------------------------------------------------------------------
bool CDataMember::SetValuePtr( EJsonDataType pType, const char * pValue, int pLen )
{
Clear();
Type = pType;
if ((pType == jtString) || (pType == jtFloat) || (pType == jtInt) || (pType == jtBool))
{
Len = (pLen == -1)? strlen(pValue) : pLen;
Value = (char*)pValue;
}
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetValue( EJsonDataType pType, const char * pValue, int pLen )
{
// Clear & Update Type
Clear();
Type = pType;
// Set new primitive value
if ((pType == jtString) || (pType == jtFloat) || (pType == jtInt) || (pType == jtBool))
{
Value = strdup( pValue );
Len = (pLen == -1)? strlen(pValue) : pLen;
}
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChObject( const char * Path )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Set as Object
Member->SetValue( jtObject, NULL );
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChArray( const char * Path )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Set as Object
Member->SetValue( jtArray, NULL );
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChStr( const char * Path, const char * Value, const int Len )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Create Value
if (!Value) {
Member->SetValue( jtString, "", 0 );
}
else {
Member->SetValue( jtString, Value, Len );
}
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChInt( const char * Path, const long Value, const char * Mask )
{
CDataMember * Member;
char ValueStr[20];
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Create Value
sprintf( ValueStr, ((Mask)? Mask : "%ld"), Value );
Member->SetValue( jtInt, ValueStr );
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChFloat( const char * Path, const double Value, const char * Mask )
{
CDataMember * Member;
char ValueStr[20];
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Create Value
sprintf( ValueStr, ((Mask)? Mask : "%lf"), Value );
Member->SetValue( jtFloat, ValueStr );
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChBool( const char * Path, const bool Value )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Create Value
Member->SetValue( jtBool, ((Value == 0)? "0" : "1") );
return true;
}
//---------------------------------------------------------------------------
bool CDataMember::SetChNull( const char * Path )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, true ))) {
return false;
}
// Create Value
Member->SetValue( jtNull, NULL );
return true;
}
//---------------------------------------------------------------------------
const char * CDataMember::GetChName( const char * Path )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, false ))) {
return NULL;
}
return Member->Name;
}
//---------------------------------------------------------------------------
EJsonDataType CDataMember::GetChType( const char * Path )
{
CDataMember * Member;
// Validate
if (!(Member = GetChild( Path, false ))) {
return jtNull;
}
return Member->Type;
}
//---------------------------------------------------------------------------
const int CDataMember::GetChLen( const char * Path )
{
CDataMember * Member;
// Validate
if ((Member = GetChild( Path, false ))) {
return Member->Len;
}
else {
return 0;
}
}
//---------------------------------------------------------------------------
const char * CDataMember::GetChStr( const char * Path, const char * Default, bool Create )
{
CDataMember * Member;
// Validate
if ((Member = GetChild( Path, Create )) &&
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
return Member->Value;
}
else if (Member && Create && (Member->Type == jtNull)) {
Member->SetStr( Default );
return Member->Value;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------
const char * CDataMember::GetChStr( const char * Path, int &Len, const char * Default, bool Create )
{
CDataMember * Member;
// Validate
if ((Member = GetChild( 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)) {
Member->SetStr( Default );
Len = Member->Len;
return Member->Value;
}
else {
Len = 0;
return Default;
}
}
//---------------------------------------------------------------------------
const long CDataMember::GetChInt( const char * Path, long Default, bool Create, const char * Mask )
{
CDataMember * Member;
// Validate
if ((Member = GetChild( Path, Create )) &&
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
return strtol( Member->Value, NULL, 10 );
}
else if (Member && Create && (Member->Type == jtNull)) {
char TempStr[20];
sprintf( TempStr, ((Mask)? Mask : "%ld"), Default );
Member->SetValue( jtInt, TempStr );
return Default;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------
const double CDataMember::GetChFloat( const char * Path, double Default, bool Create, const char * Mask )
{
CDataMember * Member;
// Validate
if ((Member = GetChild( Path, Create )) &&
((Member->Type == jtString) || (Member->Type == jtFloat) || (Member->Type == jtInt) || (Member->Type == jtBool)) ) {
return strtod( Member->Value, NULL );
}
else if (Member && Create && (Member->Type == jtNull)) {
char TempStr[20];
sprintf( TempStr, ((Mask)? Mask : "%lf"), Default );
Member->SetValue( jtFloat, TempStr );
return Default;
}
else {
return Default;
}
}
//---------------------------------------------------------------------------
const bool CDataMember::GetChBool( const char * Path, bool Default, bool Create )
{
CDataMember * Member;
// Validate
if ((Member = GetChild( Path, Create ))) {
if (Member->Type == jtString) {
return ((!*Member->Value)? false : true);
}
else if (Member->Type == jtFloat) {
return ((strtod( Member->Value, NULL ) == 0)? false : true );
}
else if (Member->Type == jtInt) {
return ((strtol( Member->Value, NULL, 10 ) == 0)? false : true );
}
else if (Member->Type == jtBool) {
return ((!strcasecmp( Member->Value, "0" ))? false : true );
}
else if ((Member->Type == jtNull) && Create) {
Member->SetValue( jtBool, ((Default)? "1" : "0") );
return Default;
}
else {
return Default;
}
}
else {
return Default;
}
}
//---------------------------------------------------------------------------