6e8fbca745
match the genesis editor version 1.3.0.653.
721 lines
19 KiB
C++
721 lines
19 KiB
C++
/****************************************************************************
|
|
Copyright (c) 2006, Radon Labs GmbH
|
|
Copyright (c) 2011-2013,WebJet Business Division,CYOU
|
|
|
|
http://www.genesis-3d.com.cn
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
****************************************************************************/
|
|
#include "stdneb.h"
|
|
#include "exception/exceptions.h"
|
|
#include "io/xmlreader.h"
|
|
|
|
namespace IO
|
|
{
|
|
__ImplementClass(IO::XmlReader, 'XMLR', IO::StreamReader);
|
|
|
|
// This static object setsup TinyXml at application startup
|
|
// (set condense white space to false). There seems to be no easy,
|
|
// alternative way (see TinyXml docs for details)
|
|
XmlReader::TinyXmlInitHelper XmlReader::initTinyXml;
|
|
|
|
using namespace Util;
|
|
|
|
using namespace Math;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
XmlReader::XmlReader() :
|
|
xmlDocument(0),
|
|
curNode(0)
|
|
|
|
{
|
|
// empty
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
XmlReader::~XmlReader()
|
|
{
|
|
if (this->IsOpen())
|
|
{
|
|
this->Close();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Opens the stream and reads the content of the stream into
|
|
TinyXML.
|
|
*/
|
|
bool
|
|
XmlReader::Open()
|
|
{
|
|
n_assert(0 == this->xmlDocument);
|
|
n_assert(0 == this->curNode);
|
|
|
|
if (StreamReader::Open())
|
|
{
|
|
// create an XML document object
|
|
this->xmlDocument = n_new(TiXmlDocument);
|
|
if (!this->xmlDocument->LoadStream(this->stream))
|
|
{
|
|
// parsing XML structure failed, provide feedback
|
|
const URI& uri = this->stream->GetURI();
|
|
if (uri.IsValid())
|
|
{
|
|
n_warning("XmlReader::Open(): failed to open stream as XML '%s'\nTinyXML error: %s!", uri.AsString().AsCharPtr(), this->xmlDocument->ErrorDesc());
|
|
}
|
|
else
|
|
{
|
|
n_warning("XmlReader::Open(): failed to open stream as XML (URI not valid)!\nTinyXML error: %s", this->xmlDocument->ErrorDesc());
|
|
}
|
|
return false;
|
|
// something happen in the LoadStream function, memory has freed
|
|
n_delete(this->xmlDocument);
|
|
this->xmlDocument = 0;
|
|
}
|
|
|
|
// since the XML document is now loaded, we can close the original stream
|
|
if (!this->streamWasOpen)
|
|
{
|
|
this->stream->Close();
|
|
}
|
|
|
|
// set the current node to the root node
|
|
this->curNode = this->xmlDocument->RootElement();
|
|
if (this->curNode)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
void
|
|
XmlReader::Close()
|
|
{
|
|
n_assert(0 != this->xmlDocument);
|
|
|
|
// delete the xml document
|
|
n_delete(this->xmlDocument);
|
|
this->xmlDocument = 0;
|
|
this->curNode = 0;
|
|
|
|
StreamReader::Close();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
This method returns the line number of the current node.
|
|
*/
|
|
int
|
|
XmlReader::GetCurrentNodeLineNumber() const
|
|
{
|
|
n_assert(this->curNode);
|
|
return this->curNode->Row();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
This method finds an xml node by path name. It can handle absolute
|
|
paths and paths relative to the current node. All the usual file system
|
|
path conventions are valid: "/" is the path separator, "." is the
|
|
current directory, ".." the parent directory.
|
|
*/
|
|
TiXmlNode*
|
|
XmlReader::FindNode(const String& path) const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(path.IsValid());
|
|
|
|
bool absPath = (path[0] == '/');
|
|
Array<String> tokens = path.Tokenize("/");
|
|
|
|
// get starting node (either root or current node)
|
|
TiXmlNode* node;
|
|
if (absPath)
|
|
{
|
|
node = this->xmlDocument;
|
|
}
|
|
else
|
|
{
|
|
n_assert(0 != this->curNode);
|
|
node = this->curNode;
|
|
}
|
|
|
|
// iterate through path components
|
|
int i;
|
|
int num = tokens.Size();
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
const String& cur = tokens[i];
|
|
if ("." == cur)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (".." == cur)
|
|
{
|
|
// go to parent directory
|
|
node = node->Parent();
|
|
if (node == this->xmlDocument)
|
|
{
|
|
SYS_EXCEPT(Exceptions::FormatException,
|
|
STRING_FORMAT("path,that is %s, points above root node.", path.AsCharPtr()),
|
|
GET_FUNCTION_NAME()
|
|
);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// find child node
|
|
node = node->FirstChild(cur.AsCharPtr());
|
|
if (0 == node)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
This method returns true if the node identified by path exists. Path
|
|
follows the normal filesystem path conventions, "/" is the separator,
|
|
".." is the parent node, "." is the current node. An absolute path
|
|
starts with a "/", a relative path doesn't.
|
|
*/
|
|
bool
|
|
XmlReader::HasNode(const String& n) const
|
|
{
|
|
return (this->FindNode(n) != 0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Get the short name (without path) of the current node.
|
|
*/
|
|
String
|
|
XmlReader::GetCurrentNodeName() const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
return String(this->curNode->Value());
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
This returns the full absolute path of the current node. Path components
|
|
are separated by slashes.
|
|
*/
|
|
String
|
|
XmlReader::GetCurrentNodePath() const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
|
|
// build bottom-up array of node names
|
|
Array<String> components;
|
|
TiXmlNode* node = this->curNode;
|
|
while (node != this->xmlDocument)
|
|
{
|
|
components.Append(node->Value());
|
|
node = node->Parent();
|
|
}
|
|
|
|
// build top down path
|
|
String path = "/";
|
|
int i;
|
|
for (i = components.Size() - 1; i >= 0; --i)
|
|
{
|
|
path.Append(components[i]);
|
|
if (i > 0)
|
|
{
|
|
path.Append("/");
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Set the node pointed to by the path string as current node. The path
|
|
may be absolute or relative, following the usual filesystem path
|
|
conventions. Separator is a slash.
|
|
*/
|
|
void
|
|
XmlReader::SetToNode(const String& path)
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(path.IsValid());
|
|
TiXmlNode* n = this->FindNode(path);
|
|
if (n)
|
|
{
|
|
this->curNode = n->ToElement();
|
|
}
|
|
else
|
|
{
|
|
SYS_EXCEPT(Exceptions::FormatException,
|
|
STRING_FORMAT("node,that is %s, is not found.", path.AsCharPtr()),
|
|
GET_FUNCTION_NAME()
|
|
);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Sets the current node to the first child node. If no child node exists,
|
|
the current node will remain unchanged and the method will return false.
|
|
If name is a valid string, only child element matching the name will
|
|
be returned. If name is empty, all child nodes will be considered.
|
|
*/
|
|
bool
|
|
XmlReader::SetToFirstChild(const String& name)
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
TiXmlElement* child = 0;
|
|
if (name.IsEmpty())
|
|
{
|
|
child = this->curNode->FirstChildElement();
|
|
}
|
|
else
|
|
{
|
|
child = this->curNode->FirstChildElement(name.AsCharPtr());
|
|
}
|
|
if (child)
|
|
{
|
|
this->curNode = child;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Sets the current node to the next sibling. If no more children exist,
|
|
the current node will be reset to the parent node and the method will
|
|
return false. If name is a valid string, only child element matching the
|
|
name will be returned. If name is empty, all child nodes will be considered.
|
|
*/
|
|
bool
|
|
XmlReader::SetToNextChild(const String& name)
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
|
|
TiXmlElement* sib = 0;
|
|
if (name.IsEmpty())
|
|
{
|
|
sib = this->curNode->NextSiblingElement();
|
|
}
|
|
else
|
|
{
|
|
sib = this->curNode->NextSiblingElement(name.AsCharPtr());
|
|
}
|
|
if (sib)
|
|
{
|
|
this->curNode = sib;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
this->SetToParent();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Sets the current node to its parent. If no parent exists, the
|
|
current node will remain unchanged and the method will return false.
|
|
*/
|
|
bool
|
|
XmlReader::SetToParent()
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
TiXmlNode* parent = this->curNode->Parent();
|
|
if (parent)
|
|
{
|
|
this->curNode = parent->ToElement();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return true if an attribute of the given name exists on the current node.
|
|
*/
|
|
bool
|
|
XmlReader::HasAttr(const char* name) const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
|
|
if (!this->curNode)
|
|
{
|
|
n_warning("XmlReader::HasAttr(): failed to open %s", this->stream->GetURI().AsString().AsCharPtr());
|
|
return false;
|
|
}
|
|
|
|
n_assert(0 != name);
|
|
return (0 != this->curNode->Attribute(name));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return array with names of all attrs on current node
|
|
*/
|
|
Array<String>
|
|
XmlReader::GetAttrs() const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
Array<String> res;
|
|
const TiXmlAttribute * attr = this->curNode->FirstAttribute();
|
|
while(0 != attr)
|
|
{
|
|
res.Append(attr->Name());
|
|
attr = attr->Next();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as string. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
String
|
|
XmlReader::GetString(const char* name) const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
n_assert(0 != name);
|
|
String str;
|
|
const char* val = this->curNode->Attribute(name);
|
|
if (0 == val)
|
|
{
|
|
SYS_EXCEPT(Exceptions::FormatException,
|
|
STRING_FORMAT("attribute '%s' doesn't exist on node '%s'!", name, this->curNode->Value()),
|
|
GET_FUNCTION_NAME()
|
|
);
|
|
}
|
|
else
|
|
{
|
|
str = val;
|
|
}
|
|
return str;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as char*. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
// - this function get one attribute and go to next attribute if exist.
|
|
const char* XmlReader::GetString_forward( const char* name )
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
n_assert(0 != name);
|
|
|
|
const char* val = this->curNode->Attribute(name);
|
|
if (0 == val)
|
|
{
|
|
SYS_EXCEPT(Exceptions::FormatException,
|
|
STRING_FORMAT("attribute '%s' doesn't exist on node '%s'!", name, this->curNode->Value()),
|
|
GET_FUNCTION_NAME()
|
|
);
|
|
}
|
|
curNode = curNode->NextSiblingElement();
|
|
return val;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as a bool. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
bool
|
|
XmlReader::GetBool(const char* name) const
|
|
{
|
|
return this->GetString(name).AsBool();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as int. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
int
|
|
XmlReader::GetInt(const char* name) const
|
|
{
|
|
return this->GetString(name).AsInt();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as float. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
float
|
|
XmlReader::GetFloat(const char* name) const
|
|
{
|
|
return this->GetString(name).AsFloat();
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as float2. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
float2
|
|
XmlReader::GetFloat2(const char* name) const
|
|
{
|
|
return this->GetString(name).AsFloat2();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as float4. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
float4
|
|
XmlReader::GetFloat4(const char* name) const
|
|
{
|
|
#if NEBULA3_XMLREADER_LEGACY_VECTORS
|
|
const String float4String = this->GetString(name);
|
|
Array<String> tokens = float4String.Tokenize(", \t");
|
|
if (tokens.Size() == 3)
|
|
{
|
|
return float4(tokens[0].AsFloat(), tokens[1].AsFloat(), tokens[2].AsFloat(), 0);
|
|
}
|
|
else if (tokens.Size() == 4)
|
|
{
|
|
return float4(tokens[0].AsFloat(), tokens[1].AsFloat(), tokens[2].AsFloat(), tokens[3].AsFloat());
|
|
}
|
|
else
|
|
{
|
|
SYS_EXCEPT(Exceptions::FormatException,
|
|
STRING_FORMAT("invalid string value for float4: %s.", float4String.AsCharPtr()),
|
|
GET_FUNCTION_NAME()
|
|
);
|
|
}
|
|
#else
|
|
return this->GetString(name).AsFloat4();
|
|
#endif
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as float3. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
float3
|
|
XmlReader::GetFloat3(const char* name) const
|
|
{
|
|
return this->GetString(name).AsFloat3();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided attribute as matrix44. If the attribute does not exist
|
|
the method will fail hard (use HasAttr() to check for its existance).
|
|
*/
|
|
matrix44
|
|
XmlReader::GetMatrix44(const char* name) const
|
|
{
|
|
return this->GetString(name).AsMatrix44();
|
|
}
|
|
|
|
|
|
Util::AssetPath XmlReader::GetAssetPath( const char* name ) const
|
|
{
|
|
return this->GetString(name).AsAssetPath();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as string. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
String
|
|
XmlReader::GetOptString(const char* name, const String& defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetString(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as bool. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
bool
|
|
XmlReader::GetOptBool(const char* name, bool defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetBool(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as int. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
int
|
|
XmlReader::GetOptInt(const char* name, int defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetInt(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as float. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
float
|
|
XmlReader::GetOptFloat(const char* name, float defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetFloat(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as float2. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
float2
|
|
XmlReader::GetOptFloat2(const char* name, const float2& defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetFloat2(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as float4. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
float4
|
|
XmlReader::GetOptFloat4(const char* name, const float4& defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetFloat4(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
Return the provided optional attribute as matrix44. If the attribute doesn't
|
|
exist, the default value will be returned.
|
|
*/
|
|
matrix44
|
|
XmlReader::GetOptMatrix44(const char* name, const matrix44& defaultValue) const
|
|
{
|
|
if (this->HasAttr(name))
|
|
{
|
|
return this->GetMatrix44(name);
|
|
}
|
|
else
|
|
{
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
bool
|
|
XmlReader::HasContent() const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(0 != this->curNode);
|
|
TiXmlNode* child = this->curNode->FirstChild();
|
|
return child && (child->Type() == TiXmlNode::TEXT);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
/**
|
|
*/
|
|
String
|
|
XmlReader::GetContent() const
|
|
{
|
|
n_assert(this->IsOpen());
|
|
n_assert(this->curNode);
|
|
TiXmlNode* child = this->curNode->FirstChild();
|
|
n_assert(child->Type() == TiXmlNode::TEXT);
|
|
return child->Value();
|
|
}
|
|
|
|
} // namespace IO
|