1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 
  8 namespace System.Xml
  9 {
 10     public class AttributeNode : Node
 11     {
 12         public AttributeNode(const System.Lex.Span& span_int fileIndex_const string& name_const string& value_) : 
 13             base(NodeKind.attributeNodespan_fileIndex_name_)value(value_)
 14         {
 15         }
 16         public const string& Value() const
 17         {
 18             return value;
 19         }
 20         public void SetValue(const string& value_)
 21         {
 22             value = value_;
 23         }
 24         public override Result<bool> Write(System.Text.CodeFormatter& formatter)
 25         {
 26             if (formatter.Error())
 27             {
 28                 return Result<bool>(ErrorId(formatter.GetErrorId()));
 29             }
 30             auto result = MakeXmlAttrValue(value);
 31             if (result.Error())
 32             {
 33                 formatter.SetErrorId(result.GetErrorId());
 34                 return Result<bool>(ErrorId(result.GetErrorId()));
 35             }
 36             formatter << " " << Name() << "=" << result.Value();
 37             return Result<bool>(true);
 38         }
 39         public override Node* Clone(bool deep) const
 40         {
 41             return new AttributeNode(Span()FileIndex()Name()value);
 42         }
 43         private string value;
 44     }
 45 
 46     public AttributeNode* MakeAttribute(const string& nameconst string& value)
 47     {
 48         return new AttributeNode(System.Lex.Span()-1namevalue);
 49     }
 50 
 51     [nodiscard]
 52     public Result<string> AttrValueEscape(const string& attributeValuechar delimiter)
 53     {
 54         string result;
 55         auto attValueResult = ToUtf32(attributeValue);
 56         if (attValueResult.Error())
 57         {
 58             return Result<string>(ErrorId(attValueResult.GetErrorId()));
 59         }
 60         ustring attValue = attValueResult.Value();
 61         for (uchar c : attValue)
 62         {
 63             switch (c)
 64             {
 65                 case '<':
 66                 {
 67                     result.Append("&lt;");
 68                     break;
 69                 }
 70                 case '&':
 71                 {
 72                     result.Append("&amp;");
 73                     break;
 74                 }
 75                 case '\"':
 76                 {
 77                     if (delimiter == '\"')result.Append("&quot;"); else result.Append('\"');
 78                     break;
 79                 }
 80                 case '\'':
 81                 {
 82                     if (delimiter == '\'') result.Append("&apos;"); else result.Append('\'');
 83                     break;
 84                 }
 85                 default:
 86                 {
 87                     if (cast<int>(c) >= 32 && cast<int>(c) < 127)
 88                     {
 89                         result.Append(cast<char>(c));
 90                     }
 91                     else
 92                     {
 93                         int codePoint = cast<int>(c);
 94                         string charText = "&#";
 95                         charText.Append(ToString(codePoint)).Append(';');
 96                         result.Append(charText);
 97                     }
 98                     break;
 99                 }
100             }
101         }
102         return Result<string>(result);
103     }
104  
105     [nodiscard]
106     public Result<string> MakeXmlAttrValue(const string& value)
107     {
108         string result;
109         if (value.Find('\"') == -1)
110         {
111             result.Append('\"');
112             auto escapeResult = AttrValueEscape(value'\"');
113             if (escapeResult.Error())
114             {
115                 return Result<string>(ErrorId(escapeResult.GetErrorId()));
116             }
117             result.Append(escapeResult.Value());
118             result.Append('\"');
119         }
120         else if (value.Find('\'') == -1)
121         {
122             result.Append('\'');
123             auto escapeResult = AttrValueEscape(value'\'');
124             if (escapeResult.Error())
125             {
126                 return Result<string>(ErrorId(escapeResult.GetErrorId()));
127             }
128             result.Append(escapeResult.Value());
129             result.Append('\'');
130         }
131         else
132         {
133             result.Append('\"');
134             auto escapeResult = AttrValueEscape(value'\"');
135             if (escapeResult.Error())
136             {
137                 return Result<string>(ErrorId(escapeResult.GetErrorId()));
138             }
139             result.Append(escapeResult.Value());
140             result.Append('\"');
141         }
142         return Result<string>(result);
143     }
144 }