1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 using System.Xml;
  9 using System.Xml.Serialization;
 10 using System.Text;
 11 using System.IO;
 12 
 13 namespace System.Xml.Serialization
 14 {
 15     internal class ScopedXmlBundleSetter
 16     {
 17         public ScopedXmlBundleSetter(XmlSerializationContext& ctx_XmlBundle* bundle) : ctx(ctx_)prevBundle(ctx.GetXmlBundle())
 18         {
 19             ctx.SetXmlBundle(bundle);
 20         }
 21         public ~ScopedXmlBundleSetter()
 22         {
 23             ctx.SetXmlBundle(prevBundle);
 24         }
 25         private XmlSerializationContext& ctx;
 26         private XmlBundle* prevBundle;
 27     }
 28     
 29     public class XmlBundle
 30     {
 31         public XmlBundle()
 32         {
 33         }
 34         public void Add(XmlSerializable* object)
 35         {
 36             Add(objecttrue);
 37         }
 38         public void Add(XmlSerializable* objectbool own)
 39         {
 40             objects.Add(object);
 41             if (own)
 42             {
 43                 ownedObjects.Add(UniquePtr<XmlSerializable>(object));
 44             }
 45             objectMap[object->ObjectId()] = object;
 46         }
 47         public long Count() const
 48         {
 49             return objects.Count();
 50         }
 51         public long OwnedCount() const
 52         {
 53             return ownedObjects.Count();
 54         }
 55         public Result<UniquePtr<System.Xml.Document>> ToXmlDocument() const
 56         {
 57             auto result = ToXml();
 58             if (result.Error())
 59             {
 60                 return Result<UniquePtr<System.Xml.Document>>(ErrorId(result.GetErrorId()));
 61             }
 62             UniquePtr<System.Xml.Document> document(new System.Xml.Document());
 63             document->AppendChild(result.Value());
 64             return Result<UniquePtr<System.Xml.Document>>(Rvalue(document));
 65         }
 66         public Result<System.Xml.Element*> ToXml() const
 67         {
 68             UniquePtr<System.Xml.Element> bundleElement(System.Xml.MakeElement("xmlBundle"));
 69             for (XmlSerializable* object : objects)
 70             {
 71                 Result<System.Xml.Element*> objectElementResult = object->ToXml("object");
 72                 if (objectElementResult.Error()) return Result<System.Xml.Element*>(ErrorId(objectElementResult.GetErrorId()));
 73                 bundleElement->AppendChild(objectElementResult.Value());
 74             }
 75             return Result<System.Xml.Element*>(bundleElement.Release());
 76         }
 77         [nodiscard]
 78         public Result<bool> FromXml(System.Xml.Document& document)
 79         {
 80             return FromXml(document.DocumentElement());
 81         }
 82         [nodiscard]
 83         public Result<bool> FromXml(System.Xml.Document& documentXmlSerializationContext& ctx)
 84         {
 85             return FromXml(document.DocumentElement()ctx);
 86         }
 87         [nodiscard]
 88         public Result<bool> FromXml(System.Xml.Element* xmlElement)
 89         {
 90             XmlSerializationContext ctx;
 91             return FromXml(xmlElementctx);
 92         }
 93         [nodiscard]
 94         public Result<bool> FromXml(System.Xml.Element* xmlElementXmlSerializationContext& ctx)
 95         {
 96             ScopedXmlBundleSetter scopedSetter(ctxthis);
 97             Result<UniquePtr<System.XPath.NodeSet>> nodeSetResult = System.XPath.EvaluateToNodeSet("object"xmlElement);
 98             if (nodeSetResult.Error())
 99             {
100                 return Result<bool>(ErrorId(nodeSetResult.GetErrorId()));
101             }
102             System.XPath.NodeSet* nodeSet = nodeSetResult.Value().Get();
103             int n = nodeSet->Count();
104             for (int i = 0; i < n; ++i;)
105             {
106                 System.Xml.Node* node = nodeSet->GetNode(i);
107                 if (node->IsElementNode())
108                 {
109                     System.Xml.Element* element = cast<System.Xml.Element*>(node);
110                     string classIdAttr = element->GetAttribute("classId");
111                     if (!classIdAttr.IsEmpty())
112                     {
113                         auto classIdResult = ParseInt(classIdAttr);
114                         if (classIdResult.Error())
115                         {
116                             return Result<bool>(ErrorId(classIdResult.GetErrorId()));
117                         }
118                         int classId = classIdResult.Value();
119                         Result<XmlSerializable*> serializableResult = XmlClassRegistry.Instance().Create(classId);
120                         if (serializableResult.Error())
121                         {
122                             return Result<bool>(ErrorId(serializableResult.GetErrorId()));
123                         }
124                         XmlSerializable* serializable = serializableResult.Value();
125                         auto result = serializable->FromXml(elementctx);
126                         if (result.Error())
127                         {
128                             return result;
129                         }
130                         Add(serializable);
131                     }
132                     else
133                     {
134                         int errorId = AllocateError("object element has no class id");
135                         return Result<bool>(ErrorId(errorId));
136                     }
137                 }
138                 else
139                 {
140                     int errorId = AllocateError("XML element expected");
141                     return Result<bool>(ErrorId(errorId));
142                 }
143             }
144             auto resolveResult = Resolve(ctx);
145             if (resolveResult.Error()) return resolveResult;
146             return Result<bool>(true);
147         }
148         public void AddPtr(XmlPtrBase* ptr)
149         {
150             ptrs.Add(ptr);
151         }
152         [nodiscard]
153         public Result<bool> Resolve(XmlSerializationContext& ctx)
154         {
155             for (XmlPtrBase* ptrBase : ptrs)
156             {
157                 XmlSerializable* s = Get(ptrBase->TargetObjectId());
158                 if (s == null && ctx.GetFlag(XmlSerializationFlags.failOnNotFoundObjects))
159                 {
160                     auto objectIdResult = ToString(ptrBase->TargetObjectId());
161                     string objectIdStr;
162                     if (!objectIdResult.Error())
163                     {
164                         objectIdStr = objectIdResult.Value();
165                     }
166                     int errorId = AllocateError("XmlBundle.Resolve: object with id " + objectIdStr + " not found from xmlBundle");
167                     return Result<bool>(ErrorId(errorId));
168                 }
169                 ptrBase->SetPtr(s);
170             }
171             return Result<bool>(true);
172         }
173         public void ClearPtrs()
174         {
175             ptrs.Clear();
176         }
177         public XmlSerializable* Get(int index) const
178         {
179             return objects[index];
180         }
181         public XmlSerializable* Release(int index)
182         {
183             return ownedObjects[index].Release();
184         }
185         public XmlSerializable* Get(const Uuid& objectId) const
186         {
187             auto it = objectMap.Find(objectId);
188             if (it != objectMap.End())
189             {
190                 return it->second;
191             }
192             else
193             {
194                 return null;
195             }
196         }
197         public const Map<UuidXmlSerializable*>& ObjectMap() const
198         {
199             return objectMap;
200         }
201         private List<XmlSerializable*> objects;
202         private List<UniquePtr<XmlSerializable>> ownedObjects;
203         private List<XmlPtrBase*> ptrs;
204         private Map<UuidXmlSerializable*> objectMap;
205     }
206 }