1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <sngxml/dom/Parser.hpp>
  7 #include <sngxml/dom/Element.hpp>
  8 #include <sngxml/dom/CharacterData.hpp>
  9 #include <sngxml/xml/XmlParserInterface.hpp>
 10 #include <soulng/util/MappedInputFile.hpp>
 11 #include <soulng/util/Unicode.hpp>
 12 #include <soulng/util/TextUtils.hpp>
 13 #include <string>
 14 #include <stack>
 15 #include <sstream>
 16 
 17 namespace sngxml { namespace dom {
 18 
 19 using namespace sngxml::xml;
 20 using namespace soulng::util;
 21 using namespace soulng::unicode;
 22 
 23 class DomDocumentHandler public XmlContentHandler
 24 {
 25 public:
 26     DomDocumentHandler();
 27     std::unique_ptr<Document> GetDocument();
 28     void StartDocument() override;
 29     void EndDocument() override;
 30     void Version(const std::u32string& xmlVersion) override;
 31     void Standalone(bool standalone) override;
 32     void Encoding(const std::u32string& encoding) override;
 33     void Text(const std::u32string& text) override;
 34     void Comment(const std::u32string& comment) override;
 35     void PI(const std::u32string& targetconst std::u32string& data) override;
 36     void CDataSection(const std::u32string& data) override;
 37     void StartElement(const std::u32string& namespaceUriconst std::u32string& localNameconst std::u32string& qualifiedNameconst Attributes& attributes) override;
 38     void EndElement(const std::u32string& namespaceUriconst std::u32string& localNameconst std::u32string& qualifiedName) override;
 39     void SkippedEntity(const std::u32string& entityName) override;
 40 private:
 41     std::unique_ptr<Document> document;
 42     std::unique_ptr<Element> currentElement;
 43     std::stack<std::std::unique_ptr<Element>>elementStack;
 44     std::u32string textContent;
 45     void AddTextContent();
 46     void AddTextContent(bool addSpace);
 47 };
 48 
 49 DomDocumentHandler::DomDocumentHandler() : document(new Document())
 50 {
 51 }
 52 
 53 std::std::unique_ptr<Document>DomDocumentHandler::GetDocument()
 54 {
 55     return std::move(document);
 56 }
 57 
 58 void DomDocumentHandler::AddTextContent()
 59 {
 60     AddTextContent(false);
 61 }
 62 
 63 void DomDocumentHandler::AddTextContent(bool addSpace)
 64 {
 65     if (currentElement)
 66     {
 67         std::u32string text = TrimAll(textContent);
 68         if (!text.empty())
 69         {
 70             if (addSpace)
 71             {
 72                 text.append(1' ');
 73             }
 74             std::unique_ptr<dom::Text> textNode(new dom::Text(text));
 75             currentElement->AppendChild(std::move(textNode));
 76         }
 77     }
 78     textContent.clear();
 79 }
 80 
 81 void DomDocumentHandler::StartDocument()
 82 {
 83     // todo
 84 }
 85 
 86 void DomDocumentHandler::EndDocument()
 87 {
 88     // todo
 89 }
 90 
 91 void DomDocumentHandler::Version(const std::u32string& xmlVersion)
 92 {
 93     document->SetXmlVersion(xmlVersion);
 94 }
 95 
 96 void DomDocumentHandler::Standalone(bool standalone)
 97 {
 98     document->SetXmlStandalone(standalone);
 99 }
100 
101 void DomDocumentHandler::Encoding(const std::u32string& encoding)
102 {
103     document->SetXmlEncoding(encoding);
104 }
105 
106 void DomDocumentHandler::Text(const std::u32string& text)
107 {
108     textContent.append(text);
109 }
110 
111 void DomDocumentHandler::Comment(const std::u32string& comment)
112 {
113     AddTextContent();
114     std::unique_ptr<dom::Comment> commentNode(new dom::Comment(comment));
115     if (currentElement)
116     {
117         currentElement->AppendChild(std::move(commentNode));
118     }
119     else
120     {
121         document->AppendChild(std::move(commentNode));
122     }
123 }
124 
125 void DomDocumentHandler::PI(const std::u32string& targetconst std::u32string& data)
126 {
127     AddTextContent();
128     std::unique_ptr<dom::ProcessingInstruction> processingInstructionNode(new dom::ProcessingInstruction(targetdata));
129     if (currentElement)
130     {
131         currentElement->AppendChild(std::move(processingInstructionNode));
132     }
133     else
134     {
135         document->AppendChild(std::move(processingInstructionNode));
136     }
137 }
138 
139 void DomDocumentHandler::CDataSection(const std::u32string& data)
140 {
141     AddTextContent();
142     std::unique_ptr<dom::CDataSection> cdataSection(new dom::CDataSection(data));
143     if (currentElement)
144     {
145         currentElement->AppendChild(std::move(cdataSection));
146     }
147     else
148     {
149         document->AppendChild(std::move(cdataSection));
150     }
151 }
152 
153 void DomDocumentHandler::StartElement(const std::u32string& namespaceUriconst std::u32string& localNameconst std::u32string& qualifiedNameconst Attributes& attributes)
154 {
155     AddTextContent(true);
156     elementStack.push(std::move(currentElement));
157     std::map<std::u32stringstd::std::unique_ptr<Attr>>attrs;
158     for (const Attribute& attr : attributes)
159     {
160         attrs[attr.QualifiedName()] = std::unique_ptr<Attr>(new Attr(attr.QualifiedName()attr.Value()));
161     }
162     currentElement.reset(new Element(qualifiedNamestd::move(attrs)));
163     currentElement->InternalSetOwnerDocument(document.get());
164     if (!namespaceUri.empty())
165     {
166         currentElement->InternalSetNamespaceUri(namespaceUri);
167     }
168 }
169 
170 void DomDocumentHandler::EndElement(const std::u32string& namespaceUriconst std::u32string& localNameconst std::u32string& qualifiedName)
171 {
172     AddTextContent();
173     std::unique_ptr<Element> parentElement = std::move(elementStack.top());
174     elementStack.pop();
175     if (parentElement)
176     {
177         parentElement->AppendChild(std::move(currentElement));
178         currentElement = std::move(parentElement);
179     }
180     else
181     {
182         document->AppendChild(std::move(currentElement));
183     }
184 }
185 
186 void DomDocumentHandler::SkippedEntity(const std::u32string& entityName)
187 {
188     // todo
189 }
190 
191 std::std::unique_ptr<Document>ParseDocument(conststd::u32string&contentconststd::string&systemId)
192 {
193     return ParseDocument(contentsystemIdFlags::none);
194 }
195 
196 std::std::unique_ptr<Document>ParseDocument(conststd::u32string&contentconststd::string&systemIdFlags flags)
197 {
198     DomDocumentHandler domDocumentHandler;
199     sngxml::xml::Flags xmlFlags = sngxml::xml::Flags::none;
200     if ((flags & Flags::debug) != Flags::none)
201     {
202         xmlFlags = xmlFlags | sngxml::xml::Flags::debug;
203     }
204     ParseXmlContent(contentsystemId&domDocumentHandlerxmlFlags);
205     return domDocumentHandler.GetDocument();
206 }
207 
208 std::std::unique_ptr<Document>ReadDocument(conststd::string&fileName)
209 {
210     return ReadDocument(fileNameFlags::none);
211 }
212 
213 std::std::unique_ptr<Document>ReadDocument(conststd::string&fileNameFlagsflags)
214 {
215     std::u32string content = ToUtf32(ReadFile(fileName));
216     return ParseDocument(contentfileNameflags);
217 }
218 
219 void SendDocument(soulng::util::TcpSocket& socketDocument& document)
220 {
221     std::stringstream sstream;
222     CodeFormatter formatter(sstream);
223     document.Write(formatter);
224     Write(socketsstream.str());
225 }
226 
227 std::std::unique_ptr<Document>ReceiveDocument(soulng::util::TcpSocket&socket)
228 {
229     std::string str = ReadStr(socket);
230     if (str.empty()) return std::unique_ptr<Document>();
231     std::u32string content = ToUtf32(str);
232     std::unique_ptr<Document> doc = ParseDocument(content"socket");
233     return doc;
234 }
235 
236 } } // namespace sngxml::dom