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 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::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::iterator Iterator = _StringToSchemaMap.begin(); Iterator != _StringToSchemaMap.end(); Iterator++) { const XMLSchemaEntry &CurEntry = *(Iterator->second); File << "\tstruct S_" << CurEntry.Name << ";" << endl; } File << endl; for(map::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 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::iterator Iterator = _StringToSchemaMap.begin(); Iterator != _StringToSchemaMap.end(); Iterator++) { const XMLSchemaEntry &CurEntry = *(Iterator->second); /*S_COLLADA::S_COLLADA(XMLNode *Node) { Vector Result; Node->GetChildrenByName("library_images", Result); if(Result.Length() == 0) { I_library_images = NULL; } else if(Result.Length() == 1) { I_library_images = new S_library_images(Result[0]); } else if(ReportXMLMismatches) { Console::File() << "Vector Mismatch: S_COLLADA.I_library_images\n"; } for(UINT ResultIndex = 0; ResultIndex < Result.Length(); ResultIndex++) { L_library_images.PushEnd(new S_library_images(Result[ResultIndex])); } }*/ 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 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; /*S_COLLADA::~S_COLLADA() { Node->GetChildrenByName("library_images", Result); if(Result.Length() == 0) { I_library_images = NULL; } else if(Result.Length() == 1) { I_library_images = new S_library_images(Result[0]); } else if(ReportXMLMismatches) { Console::File() << "Vector Mismatch: S_COLLADA.I_library_images\n"; } for(UINT ResultIndex = 0; ResultIndex < Result.Length(); ResultIndex++) { L_library_images.PushEnd(new S_library_images(Result[ResultIndex])); } }*/ 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 Data; Utility::GetFileData(Filename, Data); const UINT FileLength = Data.Length(); _Root = new XMLNode("Root"); Vector 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() == '/') { // // New leaf node // 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 << "" << 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); }