String XMLNameToCStyleName(const String &S)
{
String Result = S;
for(UINT Index = 0; Index < Result.Length(); Index++)
{
char &C = Result[Index];
if(C == ':' || C == '-')
{
C = '_';
}
}
return Result;
}
XMLDataType XMLDataTypeFromString(const String &S)
{
if(S.Length() == 0)
{
return XMLDataNone;
}
else
{
return XMLDataString;
}
}
String StructNameFromXMLDataType(XMLDataType T)
{
switch(T)
{
case XMLDataNone:
return "void";
case XMLDataString:
return "String";
default:
SignalError("Invalid data type");
return "Invalid";
}
}
void XMLSchemaEntry::ObserveNode(const XMLNode &Node)
{
Assert(Name == Node.Name, "Node name incosistent");
XMLDataType NodeType = XMLDataTypeFromString(Node.Value);
if(Type == XMLDataCount)
{
Type = NodeType;
}
else
{
if(Type == XMLDataNone)
{
Type = NodeType;
}
else
{
Assert(NodeType == XMLDataNone || Type == NodeType, "Node type inconsistent");
}
}
for(UINT AttributeIndex = 0; AttributeIndex < Node.Attributes.Length(); AttributeIndex++)
{
ObserveAttribute(Node.Attributes[AttributeIndex]);
}
Vector<XMLSchemaChildEntry> List;
Node.MakeChildEntryList(List);
for(UINT ListIndex = 0; ListIndex < List.Length(); ListIndex++)
{
ObserveChild(List[ListIndex]);
}
}
void XMLSchemaEntry::ObserveAttribute(const XMLAttribute &Attribute)
{
String CStyleAttributeName = XMLNameToCStyleName(Attribute.Name);
for(UINT AttributeIndex = 0; AttributeIndex < Attributes.Length(); AttributeIndex++)
{
XMLSchemaAttributeEntry &CurAttribute = Attributes[AttributeIndex];
if(CurAttribute.Name == CStyleAttributeName)
{
return;
}
}
Attributes.PushEnd(XMLSchemaAttributeEntry(CStyleAttributeName));
}
void XMLSchemaEntry::ObserveChild(const XMLSchemaChildEntry &Child)
{
String CStyleChildName = XMLNameToCStyleName(Child.Name);
for(UINT ChildIndex = 0; ChildIndex < Children.Length(); ChildIndex++)
{
XMLSchemaChildEntry &CurChild = Children[ChildIndex];
if(CurChild.Name == CStyleChildName)
{
CurChild.Count = Math::Max(CurChild.Count, Child.Count);
return;
}
}
Children.PushEnd(Child);
}
void XMLSchema::ObserveNode(const XMLNode &Node)
{
map<String, XMLSchemaEntry*, String::LexicographicComparison>::iterator Iterator = _StringToSchemaMap.find(Node.Name);
XMLSchemaEntry *CurSchemaEntry;
if(Iterator == _StringToSchemaMap.end())
{
CurSchemaEntry = new XMLSchemaEntry(Node.Name);
_StringToSchemaMap[Node.Name] = CurSchemaEntry;
}
else
{
CurSchemaEntry = Iterator->second;
}
CurSchemaEntry->ObserveNode(Node);
for(UINT ChildIndex = 0; ChildIndex < Node.Children.Length(); ChildIndex++)
{
ObserveNode(*(Node.Children[ChildIndex]));
}
}
void XMLSchema::SaveHeaderFile(const String &HeaderFilename, const String &Namespace)
{
ofstream File(HeaderFilename.CString());
File << "namespace " << Namespace << endl;
File << "{" << endl;
for(map<String, XMLSchemaEntry*, String::LexicographicComparison>::iterator Iterator = _StringToSchemaMap.begin();
Iterator != _StringToSchemaMap.end(); Iterator++)
{
const XMLSchemaEntry &CurEntry = *(Iterator->second);
File << "\tstruct S_" << CurEntry.Name << ";" << endl;
}
File << endl;
for(map<String, XMLSchemaEntry*, String::LexicographicComparison>::iterator Iterator = _StringToSchemaMap.begin();
Iterator != _StringToSchemaMap.end(); Iterator++)
{
const XMLSchemaEntry &CurEntry = *(Iterator->second);
File << "\tstruct S_" << CurEntry.Name << endl;
File << "\t{" << endl;
File << "\t\tS_" << CurEntry.Name << "(XMLNode *Node);" << endl;
File << "\t\t~S_" << CurEntry.Name << "();" << endl;
if(CurEntry.Type != XMLDataNone)
{
File << "\t\t" << StructNameFromXMLDataType(CurEntry.Type) << " Data;" << endl;
}
for(UINT AttributeIndex = 0; AttributeIndex < CurEntry.Attributes.Length(); AttributeIndex++)
{
const XMLSchemaAttributeEntry &CurAttribute = CurEntry.Attributes[AttributeIndex];
File << "\t\t" << StructNameFromXMLDataType(CurAttribute.Type) << " A_" << CurAttribute.Name << ";" << endl;
}
for(UINT ChildIndex = 0; ChildIndex < CurEntry.Children.Length(); ChildIndex++)
{
const XMLSchemaChildEntry &CurChild = CurEntry.Children[ChildIndex];
if(CurChild.Count == 1)
{
File << "\t\tS_" << CurChild.Name << " *I_" << CurChild.Name << ";" << endl;
}
else
{
File << "\t\tVector<S_" << CurChild.Name << "*> L_" << CurChild.Name << ";" << endl;
}
}
File << "\t};" << endl;
File << endl;
}
File << "}" << endl;
}
void XMLSchema::SaveSourceFile(const String &HeaderFilename, const String &Namespace)
{
ofstream File(HeaderFilename.CString());
File << "#include \"Main.h\"" << endl << endl;
File << "#ifdef USE_" << Namespace << endl << endl;
File << "namespace " << Namespace << endl;
File << "{" << endl;
File << "\tstatic const bool ReportXMLMismatches = true;" << endl << endl;
for(map<String, XMLSchemaEntry*, String::LexicographicComparison>::iterator Iterator = _StringToSchemaMap.begin();
Iterator != _StringToSchemaMap.end(); Iterator++)
{
const XMLSchemaEntry &CurEntry = *(Iterator->second);
File << "\tS_" << CurEntry.Name << "::S_" << CurEntry.Name << "(XMLNode *Node)" << endl;
File << "\t{" << endl;
if(CurEntry.Type == XMLDataString)
{
File << "\t\tData = Node->Value;" << endl << endl;
}
if(CurEntry.Attributes.Length() > 0)
{
for(UINT AttributeIndex = 0; AttributeIndex < CurEntry.Attributes.Length(); AttributeIndex++)
{
const XMLSchemaAttributeEntry &CurAttribute = CurEntry.Attributes[AttributeIndex];
File << "\t\tA_" << CurAttribute.Name << " = Node->GetAttributeByName(\"" << CurAttribute.Name << "\");" << endl;
}
File << endl;
}
if(CurEntry.Children.Length() > 0)
{
File << "\t\tVector<XMLNode*> Result;" << endl << endl;
for(UINT ChildIndex = 0; ChildIndex < CurEntry.Children.Length(); ChildIndex++)
{
const XMLSchemaChildEntry &CurChild = CurEntry.Children[ChildIndex];
File << "\t\tNode->GetChildrenByName(\"" << CurChild.Name << "\", Result);" << endl;
if(CurChild.Count == 1)
{
File << "\t\tif(Result.Length() == 0)" << endl;
File << "\t\t{" << endl;
File << "\t\t\tI_" << CurChild.Name << " = NULL;" << endl;
File << "\t\t}" << endl;
File << "\t\telse if(Result.Length() == 1)" << endl;
File << "\t\t{" << endl;
File << "\t\t\tI_" << CurChild.Name << " = new S_" << CurChild.Name << "(Result[0]);" << endl;
File << "\t\t}" << endl;
File << "\t\telse if(ReportXMLMismatches)" << endl;
File << "\t\t{" << endl;
File << "\t\t\tSignalError(\"Vector mismatch: S_" << CurEntry.Name << ".I_" << CurChild.Name << "\\n\");" << endl;
File << "\t\t}" << endl;
}
else
{
File << "\t\tfor(UINT ResultIndex = 0; ResultIndex < Result.Length(); ResultIndex++)" << endl;
File << "\t\t{" << endl;
File << "\t\t\tL_" << CurChild.Name << ".PushEnd(new S_" << CurChild.Name << "(Result[ResultIndex]));" << endl;
File << "\t\t}" << endl;
}
File << endl;
}
}
File << "\t}" << endl;
File << endl;
File << "\tS_" << CurEntry.Name << "::~S_" << CurEntry.Name << "()" << endl;
File << "\t{" << endl;
if(CurEntry.Children.Length() > 0)
{
for(UINT ChildIndex = 0; ChildIndex < CurEntry.Children.Length(); ChildIndex++)
{
const XMLSchemaChildEntry &CurChild = CurEntry.Children[ChildIndex];
if(CurChild.Count == 1)
{
File << "\t\tif(I_" << CurChild.Name << " != NULL)" << endl;
File << "\t\t{" << endl;
File << "\t\t\tdelete I_" << CurChild.Name << ";" << endl;
File << "\t\t}" << endl;
}
else
{
File << "\t\tL_" << CurChild.Name << ".DeleteMemory();" << endl;
}
}
}
File << "\t}" << endl;
File << endl;
}
File << "}" << endl << endl;
File << "#endif" << endl;
}
XMLFile::XMLFile()
{
_Root = NULL;
}
XMLFile::~XMLFile()
{
FreeMemory();
}
void XMLFile::FreeMemory()
{
if(_Root)
{
delete _Root;
_Root = NULL;
}
}
void XMLFile::Load(const String &Filename)
{
FreeMemory();
Vector<BYTE> Data;
Utility::GetFileData(Filename, Data);
const UINT FileLength = Data.Length();
_Root = new XMLNode("Root");
Vector<XMLNode*> Stack;
Stack.PushEnd(_Root);
bool FileDone = false;
UINT DataIndex = 0;
while(!FileDone)
{
if(Data[DataIndex] == '<')
{
DataIndex++;
String ActiveLine;
bool TerminalFound = false;
bool IsScopeClosure = false;
if(Data[DataIndex] == '/')
{
IsScopeClosure = true;
DataIndex++;
}
while(!TerminalFound)
{
char C = Data[DataIndex];
if(C == '>')
{
TerminalFound = true;
}
else
{
if(C != 0x0D && C != 0x0A)
{
ActiveLine.PushEnd(C);
}
}
DataIndex++;
}
if(IsScopeClosure)
{
if(Stack.Last()->Name != ActiveLine)
{
SignalError("Invalid scope closure");
}
Stack.PopEnd();
}
else
{
if(ActiveLine.Last() == '/')
{
ActiveLine.PopEnd();
XMLNode *NewNode = new XMLNode(ActiveLine);
Stack.Last()->AddChild(NewNode);
}
else
{
XMLNode *NewNode = new XMLNode(ActiveLine);
if(NewNode->Name == "?xml")
{
delete NewNode;
}
else
{
Stack.Last()->AddChild(NewNode);
Stack.PushEnd(NewNode);
}
}
}
}
else
{
String ActiveLine;
bool TerminalFound = false;
while(!TerminalFound && DataIndex < FileLength)
{
char C = Data[DataIndex];
if(C == '<')
{
TerminalFound = true;
}
else
{
if(C != 0x0D && C != 0x0A && (ActiveLine.Length() > 0 || C != ' '))
{
ActiveLine.PushEnd(C);
}
DataIndex++;
}
}
if(ActiveLine.Length() > 0)
{
if(Stack.Last()->Value.Length() > 0)
{
SignalError("Multiple node values");
}
Stack.Last()->Value = ActiveLine;
}
}
FileDone = (DataIndex == FileLength);
}
if(Stack.Length() != 1 || Stack[0]->Name != "Root")
{
SignalError("Invalid stack at EOF");
}
}
void XMLFile::Save(const String &Filename)
{
ofstream File(Filename.CString());
File << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
_Root->Save(File);
}
void XMLFile::SaveSchema(const String &SourceFilename, const String &HeaderFilename, const String &Namespace)
{
XMLSchema Schema;
Schema.ObserveNode(*_Root);
Schema.SaveSourceFile(SourceFilename, Namespace);
Schema.SaveHeaderFile(HeaderFilename, Namespace);
}