You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

419 lines
12 KiB

////////////////////////////////////////////////////////////////////
// SML.cpp
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#include "Common.h"
#include "SML.h"
using namespace SEACAVE;
// D E F I N E S ///////////////////////////////////////////////////
#define SML_AUTOVALUES_OFF 0
#define SML_AUTOVALUES_ON 1
#ifndef SML_AUTOVALUES
#define SML_AUTOVALUES SML_AUTOVALUES_ON
#endif
#define SML_SECTIONOPENBRACKET _T("{")
#define SML_SECTIONCLOSEBRACKET _T("}")
#define SML_NAMEOPENBRACKET _T("[")
#define SML_NAMECLOSEBRACKET _T("]")
#define SML_NAMETOKEN _T("=")
#define SML_VALUETOKEN _T("\n")
#define SML_INDENT _T("\t")
#define SML_TOKEN_SECTIONOPENBRACKET SML_SECTIONOPENBRACKET[0]
#define SML_TOKEN_SECTIONCLOSEBRACKET SML_SECTIONCLOSEBRACKET[0]
#define SML_TOKEN_NAMEOPENBRACKET SML_NAMEOPENBRACKET[0]
#define SML_TOKEN_NAMECLOSEBRACKET SML_NAMECLOSEBRACKET[0]
#define SML_TOKEN_NAMETOKEN SML_NAMETOKEN[0]
#define SML_TOKEN_VALUETOKEN SML_VALUETOKEN[0]
#define SML_TOKEN_INDENT SML_INDENT[0]
#define SML_TOKEN_IGNORECHARS _T("\n\r\t ")
// S T R U C T S ///////////////////////////////////////////////////
/*-----------------------------------------------------------*
* SML class implementation *
*-----------------------------------------------------------*/
/**
* Constructor
*/
SML::SML(const String& name)
:
m_strName(name),
m_fncInitItem(NULL),
m_fncSaveItem(NULL),
m_fncReleaseItem(NULL),
m_fncItemData(NULL)
{
}
/**
* Destructor
*/
SML::~SML()
{
Release();
}
/*----------------------------------------------------------------*/
/**
* Released all used memory for this nod and its children
*/
void SML::Release()
{
m_arrChildren.ReleaseDelete();
if (m_fncReleaseItem != NULL) {
for (SMLITEMMAP::iterator it=GetBegin(); it!=GetEnd(); ++it)
m_fncReleaseItem(it->second, m_fncItemData);
}
SMLITEMMAP::Release();
}
/*----------------------------------------------------------------*/
/**
* Load the values from a file;
* all children are generated if they do not exist
*/
bool SML::Load(const String& fileName)
{
File oStream(fileName, File::READ, File::OPEN);
if (!oStream.isOpen())
return false;
return Load(oStream);
}
bool SML::Load(ISTREAM& oStream)
{
// create the section token filter and mem-file
MemFile memFile;
TokenIStream filter(&oStream);
filter.setTrimTokens(SML_TOKEN_IGNORECHARS);
// and parse section
return ParseSection(filter, memFile);
}
bool SML::ParseSection(TokenIStream& filter, MemFile& memFile)
{
while (true) {
// find first section start (NameOpenBracket)
filter.setToken(SML_TOKEN_NAMEOPENBRACKET);
filter.readLine(memFile);
// parse values before the new section or end of section
const size_f_t posMemFile = memFile.getPos();
MemFile valuesMemFile;
TokenIStream sectionFilter(&memFile, SML_TOKEN_SECTIONCLOSEBRACKET);
const size_f_t lenValues = sectionFilter.readLine(valuesMemFile);
if (ParseValues(valuesMemFile) == false)
return false; // Parse Error: invalid values
ASSERT(valuesMemFile.getSize() == 0);
// if end of section found, return
if (!sectionFilter.isEOS()) {
// if a name open bracket was found before the EOS,
// then restore it for the parent next ParseSection()
memFile.setPos(posMemFile+(lenValues+1)*sizeof(TCHAR));
if (!filter.isEOS())
filter.restoreToken();
break;
}
else {
ASSERT(memFile.getSize()-posMemFile == lenValues*(size_f_t)sizeof(TCHAR));
ASSERT(posMemFile == 0 || memFile.getSize()-posMemFile == memFile.getSizeLeft() || memFile.getSizeLeft() == 0);
memFile.setSize(0);
}
// if no more data, return
if (filter.isEOS())
break;
// parse child section name
filter.setToken(SML_TOKEN_NAMECLOSEBRACKET);
const size_t lenName = filter.readLine(memFile);
ASSERT(!filter.isEOS());
if (lenName == 0)
return false; // Parse Error: invalid section name
const String strChildName((LPCTSTR)memFile.getData(), lenName);
memFile.growSize(-((size_f_t)(lenName*sizeof(TCHAR))));
ASSERT(memFile.getSize() == 0);
// create the child with the given name
const IDX idxChild = CreateChildUnique(strChildName);
LPSML const pChild = m_arrChildren[idxChild];
pChild->SetFncItem(m_fncInitItem, m_fncSaveItem, m_fncReleaseItem, m_fncItemData);
// parse child section
filter.setToken(SML_TOKEN_SECTIONOPENBRACKET);
filter.readLine(memFile);
filter.trimBackLine(memFile);
ASSERT(memFile.getSize() == 0);
if (pChild->ParseSection(filter, memFile) == false)
return false;
}
return true;
}
bool SML::ParseValues(MemFile& valuesMemFile)
{
if (valuesMemFile.getSize() == 0)
return true;
// loop through all name/value pairs
MemFile memLine;
TokenIStream filterLine(&valuesMemFile);
filterLine.setTrimTokens(SML_TOKEN_IGNORECHARS);
filterLine.setToken(SML_TOKEN_VALUETOKEN);
MemFile memValue;
TokenIStream filterValue(&memLine);
filterValue.setTrimTokens(SML_TOKEN_IGNORECHARS);
filterValue.setToken(SML_TOKEN_NAMETOKEN);
do {
// parse value name and value
filterLine.readLine(memLine);
filterLine.trimBackLine(memLine);
filterLine.trimFrontLine(memLine);
if (memLine.isEOF())
continue; // empty line, return
// parse value name
const size_t lenNameValueReal = (size_t)memLine.getSizeLeft();
const size_t lenName = filterValue.readLine(memValue);
#if SML_AUTOVALUES == SML_AUTOVALUES_ON
String szName;
if (filterValue.trimBackLine(memValue) == lenName || lenNameValueReal == lenName) {
// no name found, auto generate the name
szName = _T("Item") + String::ToString(size());
} else {
// read the name
szName = (LPCTSTR)memValue.getData();
memValue.setSize(0);
}
#else
filterValue.trimBackLine(memValue);
String szName = (LPCTSTR)memValue.getData();
ASSERT(!filterValue.isEOS() && !szName.IsEmpty());
if (filterValue.isEOS() || szName.IsEmpty()) {
memValue.setSize(0);
memLine.setSize(0);
filterValue.setPos(0);
continue; // Parse Error: invalid syntax ('=' not found)
}
ASSERT(szName.size() == memValue.getSizeLeft());
memValue.setSize(0);
#endif
SMLVALUE& val = operator[](szName);
// parse value
filterValue.read(memValue);
LPCTSTR szValue = filterValue.trimFrontLine(memValue);
val.val = szValue;
ASSERT((size_f_t)_tcslen(szValue) == memValue.getSizeLeft());
memValue.setSize(0);
memLine.setSize(0);
filterValue.setPos(0);
} while (!filterLine.isEOS());
// all file processed, safe to reset it to 0
valuesMemFile.setSize(0);
return true;
}
/*----------------------------------------------------------------*/
/**
* Write to a file the values of this node and its children.
* Set to false the second parameter in order not to save the empty children.
*/
bool SML::Save(const String& fileName, SAVEFLAG flags) const
{
File oStream(fileName, File::WRITE, File::CREATE | File::TRUNCATE);
if (!oStream.isOpen())
return false;
return Save(oStream, flags);
}
bool SML::Save(OSTREAM& oStream, SAVEFLAG flags) const
{
// save all values and all children
return SaveIntern(oStream, _T(""), flags);
}
bool SML::SaveBracketize(OSTREAM& oStream, const String& strIndent, SAVEFLAG flags) const
{
// save its name
oStream.print(_T("%s" SML_NAMEOPENBRACKET "%s" SML_NAMECLOSEBRACKET "\n"), strIndent.c_str(), m_strName.c_str());
oStream.print(_T("%s" SML_SECTIONOPENBRACKET "\n"), strIndent.c_str());
// save all values and all children
SaveIntern(oStream, strIndent+SML_TOKEN_INDENT, flags);
// close bracket
oStream.print(_T("%s" SML_SECTIONCLOSEBRACKET "\n"), strIndent.c_str());
return true;
}
bool SML::SaveIntern(OSTREAM& oStream, const String& strIndent, SAVEFLAG flags) const
{
const Flags& flgs = (const Flags&)flags;
if (flgs.isAnySet(SORT | SORTINV)) {
// sort values alphabetically and save them
StringArr lines(0, GetSize());
for (SMLITEMMAP::const_iterator item=GetBegin(); item!=GetEnd(); ++item)
if (!m_fncSaveItem || m_fncSaveItem((*item).second, m_fncItemData))
lines.InsertSort(String::FormatString(_T("%s%s " SML_NAMETOKEN " %s" SML_VALUETOKEN), strIndent.c_str(), (*item).first.c_str(), (*item).second.val.c_str()),
(flgs.isAnySet(SORT) ? String::CompareAlphabetically : String::CompareAlphabeticallyInv));
FOREACH(l, lines)
oStream.print(lines[l]);
} else {
// save all values directly
for (SMLITEMMAP::const_iterator item=GetBegin(); item!=GetEnd(); ++item)
if (!m_fncSaveItem || m_fncSaveItem((*item).second, m_fncItemData))
oStream.print(_T("%s%s " SML_NAMETOKEN " %s" SML_VALUETOKEN), strIndent.c_str(), (*item).first.c_str(), (*item).second.val.c_str());
}
// save now all children
bool bFirst = IsEmpty();
FOREACH(i, m_arrChildren) {
const SML* pSML = m_arrChildren[i];
// skip empty children
if (!flgs.isSet(SAVEEMPTY) && pSML->IsEmpty() && pSML->m_arrChildren.IsEmpty())
continue;
// insert a new line to separate from the above section (only for visual aspect)
if (bFirst)
bFirst = false;
else
oStream.print(_T("\n"));
// save child
pSML->SaveBracketize(oStream, strIndent, flags);
}
return true;
}
/*----------------------------------------------------------------*/
/**
* Create and insert a new child
*/
IDX SML::CreateChild(const String& strChildName)
{
return InsertChild(new SML(strChildName));
}
IDX SML::CreateChildUnique(const String& strChildName)
{
return InsertChildUnique(new SML(strChildName));
}
/*----------------------------------------------------------------*/
/**
* Insert a new child
*/
IDX SML::InsertChild(const LPSML pSML)
{
return m_arrChildren.InsertSort(pSML, SML::Compare);
}
IDX SML::InsertChildUnique(const LPSML pSML)
{
const std::pair<IDX,bool> res(m_arrChildren.InsertSortUnique(pSML, SML::Compare));
if (res.second)
delete pSML;
return res.first;
}
/*----------------------------------------------------------------*/
/**
* Remove an existing child
*/
void SML::RemoveChild(IDX idx)
{
m_arrChildren.RemoveAtMove(idx);
}
/*----------------------------------------------------------------*/
/**
* Remove and destroy an existing child
*/
void SML::DestroyChild(IDX idx)
{
delete m_arrChildren[idx];
RemoveChild(idx);
}
/*----------------------------------------------------------------*/
/**
* Retrieve an child by its name; NO_INDEX if unexistent
*/
IDX SML::GetChild(const String& name) const
{
return m_arrChildren.FindFirst(&name, SML::CompareName);
}
/*----------------------------------------------------------------*/
/**
* Retrieve an item; NULL if unexistent
*/
const SMLVALUE* SML::GetValue(const String& key) const
{
const_iterator it = Find(key);
if (it == GetEnd())
return NULL;
return &(it->second);
}
/*----------------------------------------------------------------*/
/**
* Insert or retrieve an item; initialize it if necessary
*/
SMLVALUE& SML::GetValue(const String& key)
{
bool bExisted;
SMLVALUE& val = (*Insert(key, bExisted)).second;
if (!bExisted && m_fncInitItem != NULL)
m_fncInitItem(key, val, m_fncItemData);
return val;
}
/*----------------------------------------------------------------*/
/**
* Retrieve an unnamed item by index
*/
const SMLVALUE& SML::GetValue(IDX idx) const
{
ASSERT(idx < this->size());
const String szName(_T("Item") + String::ToString(idx));
return this->at(szName);
}
/*----------------------------------------------------------------*/
/**
* Reset items' init and release functions
*/
void SML::SetFncItem(TFncInitItem fncInit, TFncSaveItem fncSave, TFncReleaseItem fncRelease, void* data)
{
m_fncInitItem = fncInit;
m_fncSaveItem = fncSave;
m_fncReleaseItem = fncRelease;
m_fncItemData = data;
}
/*----------------------------------------------------------------*/
/**
* Compare two SML nodes by name
*/
int STCALL SML::Compare(const void* l, const void* r)
{
return _tcscmp((*((const SML**)l))->GetName(), (*((const SML**)r))->GetName());
}
/*----------------------------------------------------------------*/
/**
* Compare a name and a SML node name
*/
int STCALL SML::CompareName(const void* l, const void* r)
{
return _tcscmp((*((const SML**)l))->GetName(), ((const String*)r)->c_str());
}
/*----------------------------------------------------------------*/