1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 
  9 namespace System.Xml
 10 {
 11     public class Element : ParentNode
 12     {
 13         public Element(const System.Lex.Span& span_int fileIndex_const string& name_) : base(NodeKind.elementNodespan_fileIndex_name_)
 14         {
 15         }
 16         public const Map<stringUniquePtr<AttributeNode>>& Attributes() const
 17         {
 18             return attributeMap;
 19         }
 20         public AttributeNode* GetAttributeNode(const string& attributeName) const
 21         {
 22             auto it = attributeMap.Find(attributeName);
 23             if (it != attributeMap.End())
 24             {
 25                 return it->second.Get();
 26             }
 27             else
 28             {
 29                 return null;
 30             }
 31         }
 32         public string GetAttribute(const string& name) const
 33         {
 34             AttributeNode* attributeNode = GetAttributeNode(name);
 35             if (attributeNode != null)
 36             {
 37                 return attributeNode->Value();
 38             }
 39             else
 40             {
 41                 return string();
 42             }
 43         }
 44         public void AddAttribute(AttributeNode* attributeNode)
 45         {
 46             attributeMap[attributeNode->Name()] = UniquePtr<AttributeNode>(attributeNode);
 47         }
 48         public void SetAttribute(const System.Lex.Span& spanint fileIndexconst string& nameconst string& value)
 49         {
 50             AttributeNode* attributeNode = GetAttributeNode(name);
 51             if (attributeNode != null)
 52             {
 53                 attributeNode->SetValue(value);
 54             }
 55             else
 56             {
 57                 AddAttribute(new AttributeNode(spanfileIndexnamevalue));
 58             }
 59         }
 60         public void SetAttribute(const string& nameconst string& value)
 61         {
 62             SetAttribute(System.Lex.Span()-1namevalue);
 63         }
 64         public override bool HasAttributes() const
 65         {
 66             return !attributeMap.IsEmpty();
 67         }
 68         public override void Accept(Visitor& visitor)
 69         {
 70             visitor.BeginVisit(*this);
 71             base->Accept(visitor);
 72             visitor.EndVisit(*this);
 73         }
 74         [nodiscard]
 75         public override Result<bool> Write(System.Text.CodeFormatter& formatter)
 76         {
 77             if (formatter.Error())
 78             {
 79                 return Result<bool>(ErrorId(formatter.GetErrorId()));
 80             }
 81             if (HasChildNodes())
 82             {
 83                 if (attributeMap.IsEmpty())
 84                 {
 85                     formatter << "<" << Name() << ">";
 86                 }
 87                 else
 88                 {
 89                     formatter << "<" << Name();
 90                     WriteAttributes(formatter);
 91                     formatter << ">";
 92                 }
 93                 bool prevPreserveSpace = formatter.PreserveSpace();
 94                 if (GetAttribute("xml:space") == "preserve")
 95                 {
 96                     formatter.SetPreserveSpace(true);
 97                 }
 98                 bool preserveSpace = formatter.PreserveSpace() || !HasMultilineContent();
 99                 if (!preserveSpace)
100                 {
101                     auto result = formatter.WriteLine();
102                     if (result.Error()) return result;
103                     formatter.IncIndent();
104                 }
105                 auto result = base->Write(formatter);
106                 if (result.Error()) return result;
107                 if (!preserveSpace)
108                 {
109                     formatter.DecIndent();
110                     formatter << "</" << Name() << ">" << endl();
111                 }
112                 else if (prevPreserveSpace)
113                 {
114                     formatter << "</" << Name() << ">";
115                 }
116                 else
117                 {
118                     formatter << "</" << Name() << ">" << endl();
119                 }
120                 formatter.SetPreserveSpace(prevPreserveSpace);
121             }
122             else
123             {
124                 if (attributeMap.IsEmpty())
125                 {
126                     formatter << "<" << Name() << "/>" << endl();
127                 }
128                 else
129                 {
130                     formatter << "<" << Name();
131                     WriteAttributes(formatter);
132                     formatter << "/>" << endl();
133                 }
134             }
135             return Result<bool>(true);
136         }
137         public override Node* Clone(bool deep) const
138         {
139             Element* clone = new Element(Span()FileIndex()Name());
140             for (const auto& a : attributeMap)
141             {
142                 clone->AddAttribute(cast<AttributeNode*>(a.second->Clone(deep)));
143             }
144             if (deep)
145             {
146                 Node* child = FirstChild();
147                 while (child != null)
148                 {
149                     clone->AppendChild(child->Clone(deep));
150                     child = child->Next();
151                 }
152             }
153             return clone;
154         }
155         public void WriteAttributes(System.Text.CodeFormatter& formatter)
156         {
157             for (const auto& a : attributeMap)
158             {
159                 a.second->Write(formatter);
160             }
161         }
162         public bool HasMultilineContent() const
163         {
164             if (FirstChild() != LastChild())
165             {
166                 return true;
167             }
168             Node* child = FirstChild();
169             if (child != null)
170             {
171                 if (child->IsElementNode() || child->IsDocumentNode())
172                 {
173                     return true;
174                 }
175                 if (child->ValueContainsNewLine())
176                 {
177                     return true;
178                 }
179             }
180             return false;
181         }
182         public override void WalkAttribute(NodeOperation& operation)
183         {
184             for (const auto& attribute : attributeMap)
185             {
186                 operation.Apply(attribute.second.Get());
187             }
188         }
189         private Map<stringUniquePtr<AttributeNode>> attributeMap;
190     }
191 
192     public Element* MakeElement(const string& name)
193     {
194         return new Element(System.Lex.Span()-1name);
195     }
196 }