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.IO;
  9 using System.Text;
 10 
 11 namespace System.Lex
 12 {
 13     public class XmlParsingLog : ParsingLog
 14     {
 15         public XmlParsingLog(StreamWriter& stream_) : base()formatter(stream_)
 16         {
 17             formatter.SetIndentSize(1);
 18         }
 19         public XmlParsingLog(StreamWriter& stream_int maxLineLength_) : base(maxLineLength_)formatter(stream_)
 20         {
 21             formatter.SetIndentSize(1);
 22         }
 23         public inline CodeFormatter& Formatter()
 24         {
 25             return formatter;
 26         }
 27         [nodiscard]
 28         public Result<bool> WriteElement(const ustring& elementNameconst ustring& elementContent)
 29         {
 30             if (formatter.Error())
 31             {
 32                 return Result<bool>(ErrorId(formatter.GetErrorId()));
 33             }
 34             auto convertedResult = XmlEscape(elementContent);
 35             if (convertedResult.Error())
 36             {
 37                 return Result<bool>(ErrorId(convertedResult.GetErrorId()));
 38             }
 39             ustring converted = convertedResult.Value();
 40             int convertedLength = cast<int>(converted.Length());
 41             int lineLength = 2 * cast<int>(elementName.Length()) + 5 + convertedLength;
 42             ustring s = converted;
 43             if (lineLength > MaxLineLength())
 44             {
 45                 lineLength = lineLength + 3;
 46                 s = converted.Substring(0Max(cast<int>(0)convertedLength - (lineLength - MaxLineLength()))) + u"...";
 47             }
 48             return Write(u"<" + elementName + u">" + s + u"</" + elementName + u">");
 49         }
 50         [nodiscard]
 51         public Result<bool> Write(const ustring& s)
 52         {
 53             if (formatter.Error())
 54             {
 55                 return Result<bool>(ErrorId(formatter.GetErrorId()));
 56             }
 57             auto utf8Result = ToUtf8(s);
 58             if (utf8Result.Error())
 59             {
 60                 return Result<bool>(ErrorId(utf8Result.GetErrorId()));
 61             }
 62             return formatter.WriteLine(utf8Result.Value());
 63         }
 64         public override void IncIndent()
 65         {
 66             formatter.IncIndent();
 67         }
 68         public override void DecIndent()
 69         {
 70             formatter.DecIndent();
 71         }
 72         [nodiscard]
 73         public override Result<bool> WriteBeginRule(const ustring& ruleName)
 74         {
 75             return Write(u"<" + ruleName + u">");
 76         }
 77         [nodiscard]
 78         public override Result<bool> WriteEndRule(const ustring& ruleName)
 79         {
 80             return Write(u"</" + ruleName + u">");
 81         }
 82         [nodiscard]
 83         public override Result<bool> WriteTry(const ustring& s)
 84         {
 85             return WriteElement(u"try"s);
 86         }
 87         [nodiscard]
 88         public override Result<bool> WriteSuccess(const ustring& match)
 89         {
 90             return WriteElement(u"success"match);
 91         }
 92         [nodiscard]
 93         public override Result<bool> WriteFail()
 94         {
 95             return Write(u"<fail/>");
 96         }
 97         private CodeFormatter formatter;
 98     }
 99 
100     [nodiscard]
101     public Result<ustring> XmlHexEscape(uchar c)
102     {
103         StringWriter writer;
104         auto hex = ToHexString(cast<uint>(c));
105         if (hex.Error())
106         {
107             return Result<ustring>(ErrorId(hex.GetErrorId()));
108         }
109         writer << "&#x" << hex.Value() << ";";
110         if (writer.Error())
111         {
112             return Result<ustring>(ErrorId(writer.GetErrorId()));
113         }
114         auto result = ToUtf32(writer.GetString());
115         if (result.Error())
116         {
117             return Result<ustring>(ErrorId(result.GetErrorId()));
118         }
119         return Result<ustring>(result.Value());
120     }
121 
122     [nodiscard]
123     public Result<ustring> XmlCharStr(uchar c)
124     {
125         switch (c)
126         {
127             case '&': return Result<ustring>(u"&amp;");
128             case '<': return Result<ustring>(u"&lt;");
129             case '>': return Result<ustring>(u"&gt;");
130             case '\a': return Result<ustring>(u"\\a");
131             case '\b': return Result<ustring>(u"\\b");
132             case '\f': return Result<ustring>(u"\\f");
133             case '\n': return Result<ustring>(u"\\n");
134             case '\r': return Result<ustring>(u"\\r");
135             case '\t': return Result<ustring>(u"\\t");
136             case '\v': return Result<ustring>(u"\\v");
137             default:
138             {
139                 if (cast<int>(c) >= 32 && cast<int>(c) <= 126)
140                 {
141                     return Result<ustring>(ustring(c1));
142                 }
143                 else
144                 {
145                     return XmlHexEscape(c);
146                 }
147             }
148         }
149         return Result<ustring>();
150     }
151 
152     [nodiscard]
153     public Result<ustring> XmlEscape(const ustring& s)
154     {
155         ustring result;
156         result.Reserve(2 * s.Length());
157         auto  e = s.CEnd();
158         for (auto i = s.CBegin(); i != e; ++i;)
159         {
160             auto xmlCharStr = XmlCharStr(*i);
161             if (xmlCharStr.Error())
162             {
163                 return Result<ustring>(ErrorId(xmlCharStr.GetErrorId()));
164             }
165             result.Append(xmlCharStr.Value());
166         }
167         return Result<ustring>(result);
168     }
169