move XML loading and saving into XmlFileUtil, reducing XNode
to a data structure
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
#include "song.h"
|
#include "song.h"
|
||||||
#include "Steps.h"
|
#include "Steps.h"
|
||||||
#include "XmlFile.h"
|
#include "XmlFile.h"
|
||||||
|
#include "XmlFileUtil.h"
|
||||||
#include "Course.h"
|
#include "Course.h"
|
||||||
#include "SongUtil.h"
|
#include "SongUtil.h"
|
||||||
#include "StepsUtil.h"
|
#include "StepsUtil.h"
|
||||||
@@ -391,7 +392,7 @@ void CatalogXml::Save( LoadingWindow *loading_window )
|
|||||||
xml.AppendChild( "FooterText", FOOTER_TEXT );
|
xml.AppendChild( "FooterText", FOOTER_TEXT );
|
||||||
xml.AppendChild( "FooterLink", FOOTER_LINK );
|
xml.AppendChild( "FooterLink", FOOTER_LINK );
|
||||||
|
|
||||||
xml.SaveToFile( fn, CATALOG_XSL, false );
|
XmlFileUtil::SaveToFile( &xml, fn, CATALOG_XSL, false );
|
||||||
|
|
||||||
LOG->Trace( "Done." );
|
LOG->Trace( "Done." );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
#include "IniFile.h"
|
#include "IniFile.h"
|
||||||
#include "XmlFile.h"
|
#include "XmlFile.h"
|
||||||
|
#include "XmlFileUtil.h"
|
||||||
#include "LuaManager.h"
|
#include "LuaManager.h"
|
||||||
#include "ProductInfo.h"
|
#include "ProductInfo.h"
|
||||||
#include "DateTime.h"
|
#include "DateTime.h"
|
||||||
@@ -57,7 +58,7 @@ void ExportStrings::LuaInformation()
|
|||||||
XNode *pDateNode = pNode->AppendChild( "Date" );
|
XNode *pDateNode = pNode->AppendChild( "Date" );
|
||||||
pDateNode->m_sValue = DateTime::GetNowDate().GetString();
|
pDateNode->m_sValue = DateTime::GetNowDate().GetString();
|
||||||
|
|
||||||
pNode->SaveToFile( "Lua.xml", "Lua.xsl" );
|
XmlFileUtil::SaveToFile( pNode, "Lua.xml", "Lua.xsl" );
|
||||||
|
|
||||||
delete pNode;
|
delete pNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -939,7 +939,7 @@ bool Profile::SaveStatsXmlToDir( RString sDir, bool bSignData ) const
|
|||||||
// Save stats.xml
|
// Save stats.xml
|
||||||
RString fn = sDir + STATS_XML;
|
RString fn = sDir + STATS_XML;
|
||||||
|
|
||||||
bool bSaved = xml->SaveToFile( fn, STATS_XSL, false );
|
bool bSaved = XmlFileUtil::SaveToFile( xml, fn, STATS_XSL, false );
|
||||||
|
|
||||||
SAFE_DELETE( xml );
|
SAFE_DELETE( xml );
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
#include "AdjustSync.h"
|
#include "AdjustSync.h"
|
||||||
#include "SongUtil.h"
|
#include "SongUtil.h"
|
||||||
#include "song.h"
|
#include "song.h"
|
||||||
|
#include "XmlFileUtil.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Defines
|
// Defines
|
||||||
@@ -2619,7 +2620,7 @@ void ScreenGameplay::SaveReplay()
|
|||||||
|
|
||||||
RString sFileName = ssprintf( "replay%05d.xml", iIndex );
|
RString sFileName = ssprintf( "replay%05d.xml", iIndex );
|
||||||
|
|
||||||
p->SaveToFile( "Save/"+sFileName );
|
XmlFileUtil::SaveToFile( p, "Save/"+sFileName );
|
||||||
SAFE_DELETE( p );
|
SAFE_DELETE( p );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ bool Workout::SaveToFile( RString sFile )
|
|||||||
songGenres->AppendChild( "SongGenre", *s );
|
songGenres->AppendChild( "SongGenre", *s );
|
||||||
}
|
}
|
||||||
|
|
||||||
return xml.SaveToFile( sFile );
|
return XmlFileUtil::SaveToFile( &xml, sFile );
|
||||||
}
|
}
|
||||||
|
|
||||||
// lua start
|
// lua start
|
||||||
|
|||||||
@@ -12,84 +12,6 @@
|
|||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
#include "DateTime.h"
|
#include "DateTime.h"
|
||||||
#include "Foreach.h"
|
#include "Foreach.h"
|
||||||
#include "RageFileDriverMemory.h"
|
|
||||||
|
|
||||||
static inline long XStr2Int( const char* str, long default_value = 0 )
|
|
||||||
{
|
|
||||||
return str ? atol(str) : default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char chXMLTagOpen = '<';
|
|
||||||
static const char chXMLTagClose = '>';
|
|
||||||
static const char chXMLQuestion = '?'; // used in checking for meta tags: "<?TAG ... ?/>"
|
|
||||||
static const char chXMLTagPre = '/';
|
|
||||||
static const char chXMLExclamation = '!';
|
|
||||||
static const char chXMLDash = '-';
|
|
||||||
|
|
||||||
|
|
||||||
static map<RString,RString> g_mapEntitiesToChars;
|
|
||||||
static map<char,RString> g_mapCharsToEntities;
|
|
||||||
|
|
||||||
static void InitEntities()
|
|
||||||
{
|
|
||||||
if( !g_mapEntitiesToChars.empty() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
static struct Entity
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
const char *pEntity;
|
|
||||||
}
|
|
||||||
const EntityTable[] =
|
|
||||||
{
|
|
||||||
{ '&', "amp", },
|
|
||||||
{ '\"', "quot", },
|
|
||||||
{ '\'', "apos", },
|
|
||||||
{ '<', "lt", },
|
|
||||||
{ '>', "gt", }
|
|
||||||
};
|
|
||||||
|
|
||||||
for( unsigned i = 0; i < ARRAYLEN(EntityTable); ++i )
|
|
||||||
{
|
|
||||||
const Entity &ent = EntityTable[i];
|
|
||||||
g_mapEntitiesToChars[ent.pEntity] = RString(1, ent.c);
|
|
||||||
g_mapCharsToEntities[ent.c] = ent.pEntity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct RunInitEntities
|
|
||||||
{
|
|
||||||
RunInitEntities() { InitEntities(); }
|
|
||||||
} g_RunInitEntities;
|
|
||||||
|
|
||||||
// skip spaces
|
|
||||||
static void tcsskip( const RString &s, unsigned &i )
|
|
||||||
{
|
|
||||||
i = s.find_first_not_of( " \t\r\n", i );
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool XIsEmptyString( const RString &s )
|
|
||||||
{
|
|
||||||
return s.find_first_not_of( "\r\n\t " ) == s.npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put string of (psz~end) on ps string
|
|
||||||
static void SetString( const RString &s, int iStart, int iEnd, RString* ps, bool trim = false )
|
|
||||||
{
|
|
||||||
if( trim )
|
|
||||||
{
|
|
||||||
while( iStart < iEnd && s[iStart] > 0 && isspace(s[iStart]) )
|
|
||||||
iStart++;
|
|
||||||
while( iEnd-1 >= iStart && s[iEnd-1] > 0 && isspace(s[iEnd-1]) )
|
|
||||||
iEnd--;
|
|
||||||
}
|
|
||||||
|
|
||||||
int len = iEnd - iStart;
|
|
||||||
if( len <= 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
ps->assign( s, iStart, len );
|
|
||||||
}
|
|
||||||
|
|
||||||
XNode::XNode( const XNode &cpy ):
|
XNode::XNode( const XNode &cpy ):
|
||||||
m_sName( cpy.m_sName ),
|
m_sName( cpy.m_sName ),
|
||||||
@@ -113,387 +35,6 @@ void XNode::Clear()
|
|||||||
m_attrs.clear();
|
m_attrs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// attr1="value1" attr2='value2' attr3=value3 />
|
|
||||||
// ^- return pointer
|
|
||||||
// Desc : loading attribute plain xml text
|
|
||||||
// Param : pszAttrs - xml of attributes
|
|
||||||
// pi = parser information
|
|
||||||
// Return : advanced string pointer. (error return npos)
|
|
||||||
unsigned XNode::LoadAttributes( const RString &xml, RString &sErrorOut, unsigned iOffset )
|
|
||||||
{
|
|
||||||
while( iOffset < xml.size() )
|
|
||||||
{
|
|
||||||
tcsskip( xml, iOffset );
|
|
||||||
if( iOffset >= xml.size() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// close tag
|
|
||||||
if( iOffset < xml.size() &&
|
|
||||||
(xml[iOffset] == chXMLTagClose || xml[iOffset] == chXMLTagPre || xml[iOffset] == chXMLQuestion || xml[iOffset] == chXMLDash) )
|
|
||||||
return iOffset; // well-formed tag
|
|
||||||
|
|
||||||
// XML Attr Name
|
|
||||||
unsigned iEnd = xml.find_first_of( " =", iOffset );
|
|
||||||
if( iEnd == xml.npos )
|
|
||||||
{
|
|
||||||
// error
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = ssprintf( "<%s> attribute has error ", m_sName.c_str() );
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XML Attr Name
|
|
||||||
RString sName;
|
|
||||||
SetString( xml, iOffset, iEnd, &sName );
|
|
||||||
|
|
||||||
// add new attribute
|
|
||||||
DEBUG_ASSERT( sName.size() );
|
|
||||||
pair<XAttrs::iterator,bool> it = m_attrs.insert( make_pair(sName, RString()) );
|
|
||||||
RString &sValue = it.first->second;
|
|
||||||
iOffset = iEnd;
|
|
||||||
|
|
||||||
// XML Attr Value
|
|
||||||
tcsskip( xml, iOffset );
|
|
||||||
|
|
||||||
if( iOffset < xml.size() && xml[iOffset] == '=' )
|
|
||||||
{
|
|
||||||
++iOffset;
|
|
||||||
|
|
||||||
tcsskip( xml, iOffset );
|
|
||||||
if( iOffset >= xml.size() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// if " or '
|
|
||||||
// or none quote
|
|
||||||
char quote = xml[iOffset];
|
|
||||||
if( quote == '"' || quote == '\'' )
|
|
||||||
{
|
|
||||||
++iOffset;
|
|
||||||
iEnd = xml.find( quote, iOffset );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//attr= value>
|
|
||||||
// none quote mode
|
|
||||||
iEnd = xml.find_first_of( " >", iOffset );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( iEnd == xml.npos )
|
|
||||||
{
|
|
||||||
// error
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = ssprintf( "<%s> attribute text: couldn't find matching quote", sName.c_str() );
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetString( xml, iOffset, iEnd, &sValue, true );
|
|
||||||
iOffset = iEnd;
|
|
||||||
// ATTRVALUE
|
|
||||||
ReplaceEntityText( sValue, g_mapEntitiesToChars );
|
|
||||||
|
|
||||||
if( quote == '"' || quote == '\'' )
|
|
||||||
++iOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// not well-formed tag
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// <TAG attr1="value1" attr2='value2' attr3=value3 >
|
|
||||||
// </TAG>
|
|
||||||
// or
|
|
||||||
// <TAG />
|
|
||||||
// ^- return pointer
|
|
||||||
// Desc : load xml plain text
|
|
||||||
// Param : pszXml - plain xml text
|
|
||||||
// pi = parser information
|
|
||||||
// Return : advanced string pointer (error return npos)
|
|
||||||
unsigned XNode::Load( const RString &xml, RString &sErrorOut, unsigned iOffset )
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
// <
|
|
||||||
iOffset = xml.find( chXMLTagOpen, iOffset );
|
|
||||||
if( iOffset == string::npos )
|
|
||||||
return string::npos;
|
|
||||||
|
|
||||||
// </
|
|
||||||
if( xml[iOffset+1] == chXMLTagPre )
|
|
||||||
return iOffset;
|
|
||||||
|
|
||||||
/* <!-- */
|
|
||||||
if( !xml.compare(iOffset+1, 3, "!--") )
|
|
||||||
{
|
|
||||||
iOffset += 4;
|
|
||||||
|
|
||||||
/* Find the close tag. */
|
|
||||||
unsigned iEnd = xml.find( "-->", iOffset );
|
|
||||||
if( iEnd == string::npos )
|
|
||||||
{
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = "Unterminated comment";
|
|
||||||
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip -->.
|
|
||||||
iOffset = iEnd + 3;
|
|
||||||
|
|
||||||
return Load( xml, sErrorOut, iOffset );
|
|
||||||
}
|
|
||||||
|
|
||||||
// XML Node Tag Name Open
|
|
||||||
iOffset++;
|
|
||||||
unsigned iTagEnd = xml.find_first_of( " \t\r\n/>", iOffset );
|
|
||||||
SetString( xml, iOffset, iTagEnd, &m_sName );
|
|
||||||
iOffset = iTagEnd;
|
|
||||||
|
|
||||||
// Generate XML Attributte List
|
|
||||||
iOffset = LoadAttributes( xml, sErrorOut, iOffset );
|
|
||||||
if( iOffset == string::npos )
|
|
||||||
return string::npos;
|
|
||||||
|
|
||||||
// alone tag <TAG ... /> or <?TAG ... ?> or <!-- ... -->
|
|
||||||
// current pointer: ^ ^ ^
|
|
||||||
|
|
||||||
if( iOffset < xml.size() && (xml[iOffset] == chXMLTagPre || xml[iOffset] == chXMLQuestion || xml[iOffset] == chXMLDash) )
|
|
||||||
{
|
|
||||||
iOffset++;
|
|
||||||
|
|
||||||
// skip over 2nd dash
|
|
||||||
if( iOffset < xml.size() && xml[iOffset] == chXMLDash )
|
|
||||||
iOffset++;
|
|
||||||
|
|
||||||
if( iOffset == xml.size() || xml[iOffset] != chXMLTagClose )
|
|
||||||
{
|
|
||||||
// error: <TAG ... / >
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = "Element must be closed.";
|
|
||||||
|
|
||||||
// ill-formed tag
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// well-formed tag
|
|
||||||
++iOffset;
|
|
||||||
|
|
||||||
// UGLY: We want to ignore all XML meta tags. So, since the Node we
|
|
||||||
// just loaded is a meta tag, then Load ourself again using the rest
|
|
||||||
// of the file until we reach a non-meta tag.
|
|
||||||
if( !m_sName.empty() && (m_sName[0] == chXMLQuestion || m_sName[0] == chXMLExclamation) )
|
|
||||||
iOffset = Load( xml, sErrorOut, iOffset );
|
|
||||||
|
|
||||||
return iOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open/close tag <TAG ..> ... </TAG>
|
|
||||||
// ^- current pointer
|
|
||||||
if( XIsEmptyString( m_sValue ) )
|
|
||||||
{
|
|
||||||
// Text Value
|
|
||||||
++iOffset;
|
|
||||||
unsigned iEnd = xml.find( chXMLTagOpen, iOffset );
|
|
||||||
if( iEnd == string::npos )
|
|
||||||
{
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = ssprintf( "%s must be closed with </%s>", m_sName.c_str(), m_sName.c_str() );
|
|
||||||
// error cos not exist CloseTag </TAG>
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetString( xml, iOffset, iEnd, &m_sValue, true );
|
|
||||||
|
|
||||||
iOffset = iEnd;
|
|
||||||
// TEXTVALUE reference
|
|
||||||
ReplaceEntityText( m_sValue, g_mapEntitiesToChars );
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate child nodes
|
|
||||||
while( iOffset < xml.size() )
|
|
||||||
{
|
|
||||||
XNode *node = new XNode;
|
|
||||||
|
|
||||||
iOffset = node->Load( xml, sErrorOut, iOffset );
|
|
||||||
if( !node->m_sName.empty() )
|
|
||||||
{
|
|
||||||
DEBUG_ASSERT( node->m_sName.size() );
|
|
||||||
m_childs.insert( make_pair(node->m_sName, node) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// open/close tag <TAG ..> ... </TAG>
|
|
||||||
// ^- current pointer
|
|
||||||
// CloseTag case
|
|
||||||
if( iOffset+1 < xml.size() && xml[iOffset] == chXMLTagOpen && xml[iOffset+1] == chXMLTagPre )
|
|
||||||
{
|
|
||||||
// </Close>
|
|
||||||
iOffset += 2; // C
|
|
||||||
|
|
||||||
tcsskip( xml, iOffset );
|
|
||||||
if( iOffset >= xml.size() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
unsigned iEnd = xml.find_first_of( " >", iOffset );
|
|
||||||
if( iEnd == string::npos )
|
|
||||||
{
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = ssprintf( "it must be closed with </%s>", m_sName.c_str() );
|
|
||||||
// error
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
RString closename;
|
|
||||||
SetString( xml, iOffset, iEnd, &closename );
|
|
||||||
iOffset = iEnd+1;
|
|
||||||
if( closename == this->m_sName )
|
|
||||||
{
|
|
||||||
// wel-formed open/close
|
|
||||||
// return '>' or ' ' after pointer
|
|
||||||
return iOffset;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// not welformed open/close
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = ssprintf( "'<%s> ... </%s>' is not well-formed.", m_sName.c_str(), closename.c_str() );
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Alone child Tag Loaded
|
|
||||||
{
|
|
||||||
if( XIsEmptyString( m_sValue ) && iOffset < xml.size() && xml[iOffset] != chXMLTagOpen )
|
|
||||||
{
|
|
||||||
// Text Value
|
|
||||||
unsigned iEnd = xml.find( chXMLTagOpen, iOffset );
|
|
||||||
if( iEnd == string::npos )
|
|
||||||
{
|
|
||||||
// error cos not exist CloseTag </TAG>
|
|
||||||
if( sErrorOut.empty() )
|
|
||||||
sErrorOut = ssprintf( "it must be closed with </%s>", m_sName.c_str() );
|
|
||||||
return string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetString( xml, iOffset, iEnd, &m_sValue, true );
|
|
||||||
|
|
||||||
iOffset = iEnd;
|
|
||||||
//TEXTVALUE
|
|
||||||
ReplaceEntityText( m_sValue, g_mapEntitiesToChars );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Desc : convert plain xml text from parsed xml attirbute
|
|
||||||
// Return : converted plain string
|
|
||||||
bool XNode::GetAttrXML( RageFileBasic &f, const RString &sName, const RString &sValue ) const
|
|
||||||
{
|
|
||||||
RString s(sValue);
|
|
||||||
ReplaceEntityText( s, g_mapCharsToEntities );
|
|
||||||
return f.Write(sName + "='" + s + "' ") != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Desc : convert plain xml text from parsed xml node
|
|
||||||
// Return : converted plain string
|
|
||||||
bool XNode::GetXMLInternal( RageFileBasic &f, bool bWriteTabs, int &iTabBase ) const
|
|
||||||
{
|
|
||||||
// tab
|
|
||||||
if( f.Write("\r\n") == -1 )
|
|
||||||
return false;
|
|
||||||
if( bWriteTabs )
|
|
||||||
for( int i = 0 ; i < iTabBase ; i++)
|
|
||||||
if( f.Write("\t") == -1 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// <TAG
|
|
||||||
if( f.Write("<" + m_sName) == -1 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// <TAG Attr1="Val1"
|
|
||||||
if( !m_attrs.empty() )
|
|
||||||
if( f.Write(" ") == -1 )
|
|
||||||
return false;
|
|
||||||
FOREACH_CONST_Attr( this, p )
|
|
||||||
if( !GetAttrXML(f, p->first, p->second) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( m_childs.empty() && m_sValue.empty() )
|
|
||||||
{
|
|
||||||
// <TAG Attr1="Val1"/> alone tag
|
|
||||||
if( f.Write("/>") == -1 )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// <TAG Attr1="Val1"> and get child
|
|
||||||
if( f.Write(">") == -1 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( !m_childs.empty() )
|
|
||||||
iTabBase++;
|
|
||||||
|
|
||||||
FOREACH_CONST_Child( this, p )
|
|
||||||
if( !p->GetXMLInternal( f, bWriteTabs, iTabBase ) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Text Value
|
|
||||||
if( !m_sValue.empty() )
|
|
||||||
{
|
|
||||||
if( !m_childs.empty() )
|
|
||||||
{
|
|
||||||
if( f.Write("\r\n") == -1 )
|
|
||||||
return false;
|
|
||||||
if( bWriteTabs )
|
|
||||||
for( int i = 0 ; i < iTabBase ; i++)
|
|
||||||
if( f.Write("\t") == -1 )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
RString s( m_sValue );
|
|
||||||
ReplaceEntityText( s, g_mapCharsToEntities );
|
|
||||||
if( f.Write(s) == -1 )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// </TAG> CloseTag
|
|
||||||
if( !m_childs.empty() )
|
|
||||||
{
|
|
||||||
if( f.Write("\r\n") == -1 )
|
|
||||||
return false;
|
|
||||||
if( bWriteTabs )
|
|
||||||
for( int i = 0 ; i < iTabBase-1 ; i++)
|
|
||||||
if( f.Write("\t") == -1 )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if( f.Write("</" + m_sName + ">") == -1 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if( !m_childs.empty() )
|
|
||||||
iTabBase--;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XNode::GetXML( RageFileBasic &f, bool bWriteTabs ) const
|
|
||||||
{
|
|
||||||
int iTabBase = 0;
|
|
||||||
return GetXMLInternal( f, bWriteTabs, iTabBase );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Desc : convert plain xml text from parsed xml node
|
|
||||||
// Return : converted plain string
|
|
||||||
RString XNode::GetXML() const
|
|
||||||
{
|
|
||||||
RageFileObjMem f;
|
|
||||||
int iTabBase = 0;
|
|
||||||
GetXMLInternal( f, true, iTabBase );
|
|
||||||
return f.GetString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XNode::GetValue( RString &out ) const { out = m_sValue; }
|
void XNode::GetValue( RString &out ) const { out = m_sValue; }
|
||||||
void XNode::GetValue( int &out ) const { out = atoi(m_sValue); }
|
void XNode::GetValue( int &out ) const { out = atoi(m_sValue); }
|
||||||
void XNode::GetValue( float &out ) const { out = StringToFloat(m_sValue); }
|
void XNode::GetValue( float &out ) const { out = StringToFloat(m_sValue); }
|
||||||
@@ -531,8 +72,6 @@ RString *XNode::GetAttr( const RString &attrname )
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Desc : Find child with name and return child
|
|
||||||
// Return : NULL return if no child.
|
|
||||||
XNode *XNode::GetChild( const RString &sName )
|
XNode *XNode::GetChild( const RString &sName )
|
||||||
{
|
{
|
||||||
multimap<RString, XNode*>::iterator it = m_childs.find( sName );
|
multimap<RString, XNode*>::iterator it = m_childs.find( sName );
|
||||||
@@ -598,28 +137,3 @@ void XNode::AppendAttr( const RString &sName, float value ) { AppendAttr(sName,s
|
|||||||
void XNode::AppendAttr( const RString &sName, int value ) { AppendAttr(sName,ssprintf("%d",value)); }
|
void XNode::AppendAttr( const RString &sName, int value ) { AppendAttr(sName,ssprintf("%d",value)); }
|
||||||
void XNode::AppendAttr( const RString &sName, unsigned value ) { AppendAttr(sName,ssprintf("%u",value)); }
|
void XNode::AppendAttr( const RString &sName, unsigned value ) { AppendAttr(sName,ssprintf("%u",value)); }
|
||||||
|
|
||||||
bool XNode::SaveToFile( RageFileBasic &f, const RString &sStylesheet, bool bWriteTabs ) const
|
|
||||||
{
|
|
||||||
f.PutLine( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" );
|
|
||||||
if( !sStylesheet.empty() )
|
|
||||||
f.PutLine( "<?xml-stylesheet type=\"text/xsl\" href=\"" + sStylesheet + "\"?>" );
|
|
||||||
int iTabBase = 0;
|
|
||||||
if( !this->GetXMLInternal(f, bWriteTabs, iTabBase) )
|
|
||||||
return false;
|
|
||||||
f.PutLine( "" );
|
|
||||||
if( f.Flush() == -1 )
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XNode::SaveToFile( const RString &sFile, const RString &sStylesheet, bool bWriteTabs ) const
|
|
||||||
{
|
|
||||||
RageFile f;
|
|
||||||
if( !f.Open(sFile, RageFile::WRITE) )
|
|
||||||
{
|
|
||||||
LOG->Warn( "Couldn't open %s for writing: %s", sFile.c_str(), f.GetError().c_str() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SaveToFile( f, sStylesheet, bWriteTabs );
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -57,16 +57,6 @@ public:
|
|||||||
void SetValue( unsigned v );
|
void SetValue( unsigned v );
|
||||||
void SetValue( const DateTime &v );
|
void SetValue( const DateTime &v );
|
||||||
|
|
||||||
// Load/Save XML
|
|
||||||
unsigned Load( const RString &sXml, RString &sErrorOut, unsigned iOffset = 0 );
|
|
||||||
unsigned LoadAttributes( const RString &sAttrs, RString &sErrorOut, unsigned iOffset );
|
|
||||||
bool GetAttrXML( RageFileBasic &f, const RString &sName, const RString &sValue ) const;
|
|
||||||
bool GetXML( RageFileBasic &f, bool bWriteTabs = true ) const;
|
|
||||||
RString GetXML() const;
|
|
||||||
|
|
||||||
bool SaveToFile( const RString &sFile, const RString &sStylesheet = "", bool bWriteTabs = true ) const;
|
|
||||||
bool SaveToFile( RageFileBasic &f, const RString &sStylesheet = "", bool bWriteTabs = true ) const;
|
|
||||||
|
|
||||||
// in own attribute list
|
// in own attribute list
|
||||||
const RString *GetAttr( const RString &sAttrName ) const;
|
const RString *GetAttr( const RString &sAttrName ) const;
|
||||||
RString *GetAttr( const RString &sAttrName );
|
RString *GetAttr( const RString &sAttrName );
|
||||||
@@ -103,9 +93,6 @@ public:
|
|||||||
~XNode();
|
~XNode();
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
private:
|
|
||||||
bool GetXMLInternal( RageFileBasic &f, bool bWriteTabs, int &iTabBase ) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "XmlFileUtil.h"
|
#include "XmlFileUtil.h"
|
||||||
#include "XmlFile.h"
|
#include "XmlFile.h"
|
||||||
#include "RageFile.h"
|
#include "RageFile.h"
|
||||||
|
#include "RageFileDriverMemory.h"
|
||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
#include "RageLog.h"
|
#include "RageLog.h"
|
||||||
#include "arch/Dialog/Dialog.h"
|
#include "arch/Dialog/Dialog.h"
|
||||||
@@ -13,7 +14,7 @@ bool XmlFileUtil::LoadFromFileShowErrors( XNode &xml, RageFileBasic &f )
|
|||||||
if( f.Read( s ) == -1 )
|
if( f.Read( s ) == -1 )
|
||||||
sError = f.GetError();
|
sError = f.GetError();
|
||||||
else
|
else
|
||||||
xml.Load( s, sError );
|
Load( &xml, s, sError );
|
||||||
if( sError.empty() )
|
if( sError.empty() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -43,6 +44,485 @@ bool XmlFileUtil::LoadFromFileShowErrors( XNode &xml, const RString &sFile )
|
|||||||
return bSuccess;
|
return bSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char chXMLTagOpen = '<';
|
||||||
|
static const char chXMLTagClose = '>';
|
||||||
|
static const char chXMLQuestion = '?'; // used in checking for meta tags: "<?TAG ... ?/>"
|
||||||
|
static const char chXMLTagPre = '/';
|
||||||
|
static const char chXMLExclamation = '!';
|
||||||
|
static const char chXMLDash = '-';
|
||||||
|
|
||||||
|
static map<RString,RString> g_mapEntitiesToChars;
|
||||||
|
static map<char,RString> g_mapCharsToEntities;
|
||||||
|
|
||||||
|
// XXX not called
|
||||||
|
static void InitEntities()
|
||||||
|
{
|
||||||
|
if( !g_mapEntitiesToChars.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
static struct Entity
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
const char *pEntity;
|
||||||
|
}
|
||||||
|
const EntityTable[] =
|
||||||
|
{
|
||||||
|
{ '&', "amp", },
|
||||||
|
{ '\"', "quot", },
|
||||||
|
{ '\'', "apos", },
|
||||||
|
{ '<', "lt", },
|
||||||
|
{ '>', "gt", }
|
||||||
|
};
|
||||||
|
|
||||||
|
for( unsigned i = 0; i < ARRAYLEN(EntityTable); ++i )
|
||||||
|
{
|
||||||
|
const Entity &ent = EntityTable[i];
|
||||||
|
g_mapEntitiesToChars[ent.pEntity] = RString(1, ent.c);
|
||||||
|
g_mapCharsToEntities[ent.c] = ent.pEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// skip spaces
|
||||||
|
static void tcsskip( const RString &s, unsigned &i )
|
||||||
|
{
|
||||||
|
i = s.find_first_not_of( " \t\r\n", i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool XIsEmptyString( const RString &s )
|
||||||
|
{
|
||||||
|
return s.find_first_not_of( "\r\n\t " ) == s.npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put string of (psz~end) on ps string
|
||||||
|
static void SetString( const RString &s, int iStart, int iEnd, RString* ps, bool trim = false )
|
||||||
|
{
|
||||||
|
if( trim )
|
||||||
|
{
|
||||||
|
while( iStart < iEnd && s[iStart] > 0 && isspace(s[iStart]) )
|
||||||
|
iStart++;
|
||||||
|
while( iEnd-1 >= iStart && s[iEnd-1] > 0 && isspace(s[iEnd-1]) )
|
||||||
|
iEnd--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = iEnd - iStart;
|
||||||
|
if( len <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ps->assign( s, iStart, len );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// attr1="value1" attr2='value2' attr3=value3 />
|
||||||
|
// ^- return pointer
|
||||||
|
// Desc : loading attribute plain xml text
|
||||||
|
// Param : pszAttrs - xml of attributes
|
||||||
|
// pi = parser information
|
||||||
|
// Return : advanced string pointer. (error return npos)
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
unsigned LoadAttributes( XNode *pNode, const RString &xml, RString &sErrorOut, unsigned iOffset )
|
||||||
|
{
|
||||||
|
while( iOffset < xml.size() )
|
||||||
|
{
|
||||||
|
tcsskip( xml, iOffset );
|
||||||
|
if( iOffset >= xml.size() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// close tag
|
||||||
|
if( iOffset < xml.size() &&
|
||||||
|
(xml[iOffset] == chXMLTagClose || xml[iOffset] == chXMLTagPre || xml[iOffset] == chXMLQuestion || xml[iOffset] == chXMLDash) )
|
||||||
|
return iOffset; // well-formed tag
|
||||||
|
|
||||||
|
// XML Attr Name
|
||||||
|
unsigned iEnd = xml.find_first_of( " =", iOffset );
|
||||||
|
if( iEnd == xml.npos )
|
||||||
|
{
|
||||||
|
// error
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = ssprintf( "<%s> attribute has error ", pNode->m_sName.c_str() );
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML Attr Name
|
||||||
|
RString sName;
|
||||||
|
SetString( xml, iOffset, iEnd, &sName );
|
||||||
|
|
||||||
|
// add new attribute
|
||||||
|
DEBUG_ASSERT( sName.size() );
|
||||||
|
pair<XAttrs::iterator,bool> it = pNode->m_attrs.insert( make_pair(sName, RString()) );
|
||||||
|
RString &sValue = it.first->second;
|
||||||
|
iOffset = iEnd;
|
||||||
|
|
||||||
|
// XML Attr Value
|
||||||
|
tcsskip( xml, iOffset );
|
||||||
|
|
||||||
|
if( iOffset < xml.size() && xml[iOffset] == '=' )
|
||||||
|
{
|
||||||
|
++iOffset;
|
||||||
|
|
||||||
|
tcsskip( xml, iOffset );
|
||||||
|
if( iOffset >= xml.size() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if " or '
|
||||||
|
// or none quote
|
||||||
|
char quote = xml[iOffset];
|
||||||
|
if( quote == '"' || quote == '\'' )
|
||||||
|
{
|
||||||
|
++iOffset;
|
||||||
|
iEnd = xml.find( quote, iOffset );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//attr= value>
|
||||||
|
// none quote mode
|
||||||
|
iEnd = xml.find_first_of( " >", iOffset );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( iEnd == xml.npos )
|
||||||
|
{
|
||||||
|
// error
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = ssprintf( "<%s> attribute text: couldn't find matching quote", sName.c_str() );
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetString( xml, iOffset, iEnd, &sValue, true );
|
||||||
|
iOffset = iEnd;
|
||||||
|
// ATTRVALUE
|
||||||
|
ReplaceEntityText( sValue, g_mapEntitiesToChars );
|
||||||
|
|
||||||
|
if( quote == '"' || quote == '\'' )
|
||||||
|
++iOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not well-formed tag
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// <TAG attr1="value1" attr2='value2' attr3=value3 >
|
||||||
|
// </TAG>
|
||||||
|
// or
|
||||||
|
// <TAG />
|
||||||
|
// ^- return pointer
|
||||||
|
// Desc : load xml plain text
|
||||||
|
// Param : pszXml - plain xml text
|
||||||
|
// pi = parser information
|
||||||
|
// Return : advanced string pointer (error return npos)
|
||||||
|
unsigned XmlFileUtil::Load( XNode *pNode, const RString &xml, RString &sErrorOut, unsigned iOffset )
|
||||||
|
{
|
||||||
|
pNode->Clear();
|
||||||
|
|
||||||
|
// <
|
||||||
|
iOffset = xml.find( chXMLTagOpen, iOffset );
|
||||||
|
if( iOffset == string::npos )
|
||||||
|
return string::npos;
|
||||||
|
|
||||||
|
// </
|
||||||
|
if( xml[iOffset+1] == chXMLTagPre )
|
||||||
|
return iOffset;
|
||||||
|
|
||||||
|
/* <!-- */
|
||||||
|
if( !xml.compare(iOffset+1, 3, "!--") )
|
||||||
|
{
|
||||||
|
iOffset += 4;
|
||||||
|
|
||||||
|
/* Find the close tag. */
|
||||||
|
unsigned iEnd = xml.find( "-->", iOffset );
|
||||||
|
if( iEnd == string::npos )
|
||||||
|
{
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = "Unterminated comment";
|
||||||
|
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip -->.
|
||||||
|
iOffset = iEnd + 3;
|
||||||
|
|
||||||
|
return Load( pNode, xml, sErrorOut, iOffset );
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML Node Tag Name Open
|
||||||
|
iOffset++;
|
||||||
|
unsigned iTagEnd = xml.find_first_of( " \t\r\n/>", iOffset );
|
||||||
|
SetString( xml, iOffset, iTagEnd, &pNode->m_sName );
|
||||||
|
iOffset = iTagEnd;
|
||||||
|
|
||||||
|
// Generate XML Attributte List
|
||||||
|
iOffset = LoadAttributes( pNode, xml, sErrorOut, iOffset );
|
||||||
|
if( iOffset == string::npos )
|
||||||
|
return string::npos;
|
||||||
|
|
||||||
|
// alone tag <TAG ... /> or <?TAG ... ?> or <!-- ... -->
|
||||||
|
// current pointer: ^ ^ ^
|
||||||
|
|
||||||
|
if( iOffset < xml.size() && (xml[iOffset] == chXMLTagPre || xml[iOffset] == chXMLQuestion || xml[iOffset] == chXMLDash) )
|
||||||
|
{
|
||||||
|
iOffset++;
|
||||||
|
|
||||||
|
// skip over 2nd dash
|
||||||
|
if( iOffset < xml.size() && xml[iOffset] == chXMLDash )
|
||||||
|
iOffset++;
|
||||||
|
|
||||||
|
if( iOffset == xml.size() || xml[iOffset] != chXMLTagClose )
|
||||||
|
{
|
||||||
|
// error: <TAG ... / >
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = "Element must be closed.";
|
||||||
|
|
||||||
|
// ill-formed tag
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// well-formed tag
|
||||||
|
++iOffset;
|
||||||
|
|
||||||
|
// UGLY: We want to ignore all XML meta tags. So, since the Node we
|
||||||
|
// just loaded is a meta tag, then Load ourself again using the rest
|
||||||
|
// of the file until we reach a non-meta tag.
|
||||||
|
if( !pNode->m_sName.empty() && (pNode->m_sName[0] == chXMLQuestion || pNode->m_sName[0] == chXMLExclamation) )
|
||||||
|
iOffset = Load( pNode, xml, sErrorOut, iOffset );
|
||||||
|
|
||||||
|
return iOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open/close tag <TAG ..> ... </TAG>
|
||||||
|
// ^- current pointer
|
||||||
|
if( XIsEmptyString(pNode->m_sValue) )
|
||||||
|
{
|
||||||
|
// Text Value
|
||||||
|
++iOffset;
|
||||||
|
unsigned iEnd = xml.find( chXMLTagOpen, iOffset );
|
||||||
|
if( iEnd == string::npos )
|
||||||
|
{
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = ssprintf( "%s must be closed with </%s>", pNode->m_sName.c_str(), pNode->m_sName.c_str() );
|
||||||
|
// error cos not exist CloseTag </TAG>
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetString( xml, iOffset, iEnd, &pNode->m_sValue, true );
|
||||||
|
|
||||||
|
iOffset = iEnd;
|
||||||
|
// TEXTVALUE reference
|
||||||
|
ReplaceEntityText( pNode->m_sValue, g_mapEntitiesToChars );
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate child nodes
|
||||||
|
while( iOffset < xml.size() )
|
||||||
|
{
|
||||||
|
XNode *node = new XNode;
|
||||||
|
|
||||||
|
iOffset = Load( node, xml, sErrorOut, iOffset );
|
||||||
|
if( !node->m_sName.empty() )
|
||||||
|
{
|
||||||
|
DEBUG_ASSERT( node->m_sName.size() );
|
||||||
|
pNode->m_childs.insert( make_pair(node->m_sName, node) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open/close tag <TAG ..> ... </TAG>
|
||||||
|
// ^- current pointer
|
||||||
|
// CloseTag case
|
||||||
|
if( iOffset+1 < xml.size() && xml[iOffset] == chXMLTagOpen && xml[iOffset+1] == chXMLTagPre )
|
||||||
|
{
|
||||||
|
// </Close>
|
||||||
|
iOffset += 2; // C
|
||||||
|
|
||||||
|
tcsskip( xml, iOffset );
|
||||||
|
if( iOffset >= xml.size() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsigned iEnd = xml.find_first_of( " >", iOffset );
|
||||||
|
if( iEnd == string::npos )
|
||||||
|
{
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = ssprintf( "it must be closed with </%s>", pNode->m_sName.c_str() );
|
||||||
|
// error
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
RString closename;
|
||||||
|
SetString( xml, iOffset, iEnd, &closename );
|
||||||
|
iOffset = iEnd+1;
|
||||||
|
if( closename == pNode->m_sName )
|
||||||
|
{
|
||||||
|
// wel-formed open/close
|
||||||
|
// return '>' or ' ' after pointer
|
||||||
|
return iOffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// not welformed open/close
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = ssprintf( "'<%s> ... </%s>' is not well-formed.", pNode->m_sName.c_str(), closename.c_str() );
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Alone child Tag Loaded
|
||||||
|
{
|
||||||
|
if( XIsEmptyString(pNode->m_sValue) && iOffset < xml.size() && xml[iOffset] != chXMLTagOpen )
|
||||||
|
{
|
||||||
|
// Text Value
|
||||||
|
unsigned iEnd = xml.find( chXMLTagOpen, iOffset );
|
||||||
|
if( iEnd == string::npos )
|
||||||
|
{
|
||||||
|
// error cos not exist CloseTag </TAG>
|
||||||
|
if( sErrorOut.empty() )
|
||||||
|
sErrorOut = ssprintf( "it must be closed with </%s>", pNode->m_sName.c_str() );
|
||||||
|
return string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetString( xml, iOffset, iEnd, &pNode->m_sValue, true );
|
||||||
|
|
||||||
|
iOffset = iEnd;
|
||||||
|
//TEXTVALUE
|
||||||
|
ReplaceEntityText( pNode->m_sValue, g_mapEntitiesToChars );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool GetAttrXML( RageFileBasic &f, const RString &sName, const RString &sValue )
|
||||||
|
{
|
||||||
|
RString s(sValue);
|
||||||
|
ReplaceEntityText( s, g_mapCharsToEntities );
|
||||||
|
return f.Write(sName + "='" + s + "' ") != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetXMLInternal( const XNode *pNode, RageFileBasic &f, bool bWriteTabs, int &iTabBase )
|
||||||
|
{
|
||||||
|
// tab
|
||||||
|
if( f.Write("\r\n") == -1 )
|
||||||
|
return false;
|
||||||
|
if( bWriteTabs )
|
||||||
|
for( int i = 0 ; i < iTabBase ; i++)
|
||||||
|
if( f.Write("\t") == -1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// <TAG
|
||||||
|
if( f.Write("<" + pNode->m_sName) == -1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// <TAG Attr1="Val1"
|
||||||
|
if( !pNode->m_attrs.empty() )
|
||||||
|
if( f.Write(" ") == -1 )
|
||||||
|
return false;
|
||||||
|
FOREACH_CONST_Attr( pNode, p )
|
||||||
|
if( !GetAttrXML(f, p->first, p->second) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( pNode->m_childs.empty() && pNode->m_sValue.empty() )
|
||||||
|
{
|
||||||
|
// <TAG Attr1="Val1"/> alone tag
|
||||||
|
if( f.Write("/>") == -1 )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// <TAG Attr1="Val1"> and get child
|
||||||
|
if( f.Write(">") == -1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( !pNode->m_childs.empty() )
|
||||||
|
iTabBase++;
|
||||||
|
|
||||||
|
FOREACH_CONST_Child( pNode, p )
|
||||||
|
if( !GetXMLInternal( pNode, f, bWriteTabs, iTabBase ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Text Value
|
||||||
|
if( !pNode->m_sValue.empty() )
|
||||||
|
{
|
||||||
|
if( !pNode->m_childs.empty() )
|
||||||
|
{
|
||||||
|
if( f.Write("\r\n") == -1 )
|
||||||
|
return false;
|
||||||
|
if( bWriteTabs )
|
||||||
|
for( int i = 0 ; i < iTabBase ; i++)
|
||||||
|
if( f.Write("\t") == -1 )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RString s( pNode->m_sValue );
|
||||||
|
ReplaceEntityText( s, g_mapCharsToEntities );
|
||||||
|
if( f.Write(s) == -1 )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// </TAG> CloseTag
|
||||||
|
if( !pNode->m_childs.empty() )
|
||||||
|
{
|
||||||
|
if( f.Write("\r\n") == -1 )
|
||||||
|
return false;
|
||||||
|
if( bWriteTabs )
|
||||||
|
for( int i = 0 ; i < iTabBase-1 ; i++)
|
||||||
|
if( f.Write("\t") == -1 )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if( f.Write("</" + pNode->m_sName + ">") == -1 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( !pNode->m_childs.empty() )
|
||||||
|
iTabBase--;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XmlFileUtil::GetXML( const XNode *pNode, RageFileBasic &f, bool bWriteTabs )
|
||||||
|
{
|
||||||
|
int iTabBase = 0;
|
||||||
|
return GetXMLInternal( pNode, f, bWriteTabs, iTabBase );
|
||||||
|
}
|
||||||
|
|
||||||
|
RString XmlFileUtil::GetXML( const XNode *pNode )
|
||||||
|
{
|
||||||
|
RageFileObjMem f;
|
||||||
|
int iTabBase = 0;
|
||||||
|
GetXMLInternal( pNode, f, true, iTabBase );
|
||||||
|
return f.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XmlFileUtil::SaveToFile( const XNode *pNode, RageFileBasic &f, const RString &sStylesheet, bool bWriteTabs )
|
||||||
|
{
|
||||||
|
f.PutLine( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" );
|
||||||
|
if( !sStylesheet.empty() )
|
||||||
|
f.PutLine( "<?xml-stylesheet type=\"text/xsl\" href=\"" + sStylesheet + "\"?>" );
|
||||||
|
int iTabBase = 0;
|
||||||
|
if( !GetXMLInternal(pNode, f, bWriteTabs, iTabBase) )
|
||||||
|
return false;
|
||||||
|
f.PutLine( "" );
|
||||||
|
if( f.Flush() == -1 )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XmlFileUtil::SaveToFile( const XNode *pNode, const RString &sFile, const RString &sStylesheet, bool bWriteTabs )
|
||||||
|
{
|
||||||
|
RageFile f;
|
||||||
|
if( !f.Open(sFile, RageFile::WRITE) )
|
||||||
|
{
|
||||||
|
LOG->Warn( "Couldn't open %s for writing: %s", sFile.c_str(), f.GetError().c_str() );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SaveToFile( pNode, f, sStylesheet, bWriteTabs );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (c) 2001-2004 Chris Danford
|
* (c) 2001-2004 Chris Danford
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ namespace XmlFileUtil
|
|||||||
{
|
{
|
||||||
bool LoadFromFileShowErrors( XNode &xml, const RString &sFile );
|
bool LoadFromFileShowErrors( XNode &xml, const RString &sFile );
|
||||||
bool LoadFromFileShowErrors( XNode &xml, RageFileBasic &f );
|
bool LoadFromFileShowErrors( XNode &xml, RageFileBasic &f );
|
||||||
|
|
||||||
|
// Load/Save XML
|
||||||
|
unsigned Load( XNode *pNode, const RString &sXml, RString &sErrorOut, unsigned iOffset = 0 );
|
||||||
|
bool GetXML( const XNode *pNode, RageFileBasic &f, bool bWriteTabs = true );
|
||||||
|
RString GetXML( const XNode *pNode );
|
||||||
|
bool SaveToFile( const XNode *pNode, const RString &sFile, const RString &sStylesheet = "", bool bWriteTabs = true );
|
||||||
|
bool SaveToFile( const XNode *pNode, RageFileBasic &f, const RString &sStylesheet = "", bool bWriteTabs = true );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user