1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <cpp2cm/cpp2cm/Merge.hpp>
  7 #include <sngxml/xpath/XPathEvaluate.hpp>
  8 #include <sngxml/dom/Element.hpp>
  9 
 10 namespace cpp2cm {
 11 
 12 sngxml::dom::Node* AddElement(sngxml::dom::Element* sourceElementsngxml::dom::Element* parentElementsngxml::dom::Document* targetDocbool deepconst std::u32string& container)
 13 {
 14     sngxml::dom::Element* originalParent = parentElement;
 15     if (parentElement == nullptr)
 16     {
 17         if (sourceElement->Parent() && sourceElement->Parent()->GetNodeType() == sngxml::dom::NodeType::elementNode)
 18         {
 19             parentElement = static_cast<sngxml::dom::Element*>(sourceElement->Parent());
 20         }
 21     }
 22     else
 23     {
 24         if (parentElement->Parent() && parentElement->Parent()->GetNodeType() == sngxml::dom::NodeType::elementNode)
 25         {
 26             parentElement = static_cast<sngxml::dom::Element*>(parentElement->Parent());
 27         }
 28     }
 29     if (parentElement)
 30     {
 31         std::u32string parentId = parentElement->GetAttribute(U"id");
 32         if (!parentId.empty())
 33         {
 34             sngxml::dom::Element* targetParentElement = targetDoc->GetElementById(parentId);
 35             if (!targetParentElement)
 36             {
 37                 sngxml::dom::Node* newNode = AddElement(parentElementparentElementtargetDocfalsecontainer);
 38                 if (newNode)
 39                 {
 40                     if (newNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
 41                     {
 42                         targetParentElement = static_cast<sngxml::dom::Element*>(newNode);
 43                     }
 44                 }
 45             }
 46             if (targetParentElement)
 47             {
 48                 if (originalParent && originalParent->Name() == container)
 49                 {
 50                     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(containertargetParentElement);
 51                     if (result)
 52                     {
 53                         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 54                         {
 55                             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 56                             int n = nodeSet->Length();
 57                             if (n == 0)
 58                             {
 59                                 sngxml::dom::Node* parentNode = targetParentElement->AppendChild(originalParent->CloneNode(false));
 60                                 if (parentNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
 61                                 {
 62                                     targetParentElement = static_cast<sngxml::dom::Element*>(parentNode);
 63                                 }
 64                             }
 65                             else if (n == 1)
 66                             {
 67                                 sngxml::dom::Node* node = (*nodeSet)[0];
 68                                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 69                                 {
 70                                     targetParentElement = static_cast<sngxml::dom::Element*>(node);
 71                                 }
 72                             }
 73                         }
 74                     }
 75                 }
 76                 return targetParentElement->AppendChild(sourceElement->CloneNode(deep));
 77             }
 78         }
 79         else
 80         {
 81             return AddElement(sourceElementparentElementtargetDocdeepcontainer);
 82         }
 83     }
 84     return nullptr;
 85 }
 86 
 87 void RemoveElement(sngxml::dom::Element* element)
 88 {
 89     if (element->Parent() && element->Parent()->GetNodeType() == sngxml::dom::NodeType::elementNode)
 90     {
 91         sngxml::dom::Element* parentElement = static_cast<sngxml::dom::Element*>(element->Parent());
 92         parentElement->RemoveChild(element);
 93     }
 94 }
 95 
 96 void MergeConstructors(sngxml::dom::Element* sourceElementsngxml::dom::Document* targetDoc)
 97 {
 98     std::vector<sngxml::dom::Element*> elementsToRemove;
 99     std::vector<sngxml::dom::Element*> elementsToAdd;
100     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"constructors/constructor"sourceElement);
101     if (result)
102     {
103         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
104         {
105             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
106             int n = nodeSet->Length();
107             for (int i = 0; i < n; ++i)
108             {
109                 sngxml::dom::Node* node = (*nodeSet)[i];
110                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
111                 {
112                     sngxml::dom::Element* constructorElement = static_cast<sngxml::dom::Element*>(node);
113                     std::u32string constructorId = constructorElement->GetAttribute(U"id");
114                     if (!constructorId.empty())
115                     {
116                         sngxml::dom::Element* targetConstructorElement = targetDoc->GetElementById(constructorId);
117                         if (targetConstructorElement)
118                         {
119                             std::u32string targetAttr = targetConstructorElement->GetAttribute(U"target");
120                             if (targetAttr.empty())
121                             {
122                                 elementsToRemove.push_back(targetConstructorElement);
123                                 elementsToAdd.push_back(constructorElement);
124                             }
125                         }
126                         else
127                         {
128                             elementsToAdd.push_back(constructorElement);
129                         }
130                     }
131                 }
132             }
133         }
134     }
135     for (sngxml::dom::Element* elementToRemove : elementsToRemove)
136     {
137         RemoveElement(elementToRemove);
138     }
139     for (sngxml::dom::Element* elementToAdd : elementsToAdd)
140     {
141         AddElement(elementToAddnullptrtargetDoctrueU"constructors");
142     }
143 }
144 
145 void MergeFunctions(sngxml::dom::Element* sourceElementsngxml::dom::Document* targetDoc)
146 {
147     std::vector<sngxml::dom::Element*> elementsToRemove;
148     std::vector<sngxml::dom::Element*> elementsToAdd;
149     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"functions/function"sourceElement);
150     if (result)
151     {
152         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
153         {
154             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
155             int n = nodeSet->Length();
156             for (int i = 0; i < n; ++i)
157             {
158                 sngxml::dom::Node* node = (*nodeSet)[i];
159                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
160                 {
161                     sngxml::dom::Element* functionElement = static_cast<sngxml::dom::Element*>(node);
162                     std::u32string functionId = functionElement->GetAttribute(U"id");
163                     if (!functionId.empty())
164                     {
165                         sngxml::dom::Element* targetFunctionElement = targetDoc->GetElementById(functionId);
166                         if (targetFunctionElement)
167                         {
168                             std::u32string targetAttr = targetFunctionElement->GetAttribute(U"target");
169                             if (targetAttr.empty())
170                             {
171                                 elementsToRemove.push_back(targetFunctionElement);
172                                 elementsToAdd.push_back(functionElement);
173                             }
174                         }
175                         else
176                         {
177                             elementsToAdd.push_back(functionElement);
178                         }
179                     }
180                 }
181             }
182         }
183     }
184     for (sngxml::dom::Element* elementToRemove : elementsToRemove)
185     {
186         RemoveElement(elementToRemove);
187     }
188     for (sngxml::dom::Element* elementToAdd : elementsToAdd)
189     {
190         AddElement(elementToAddnullptrtargetDoctrueU"functions");
191     }
192 }
193 
194 void MergeTypedefs(sngxml::dom::Element* sourceElementsngxml::dom::Document* targetDoc)
195 {
196     std::vector<sngxml::dom::Element*> elementsToRemove;
197     std::vector<sngxml::dom::Element*> elementsToAdd;
198     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"typedefs/typedef"sourceElement);
199     if (result)
200     {
201         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
202         {
203             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
204             int n = nodeSet->Length();
205             for (int i = 0; i < n; ++i)
206             {
207                 sngxml::dom::Node* node = (*nodeSet)[i];
208                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
209                 {
210                     sngxml::dom::Element* typedefElement = static_cast<sngxml::dom::Element*>(node);
211                     std::u32string typedefId = typedefElement->GetAttribute(U"id");
212                     if (!typedefId.empty())
213                     {
214                         sngxml::dom::Element* targetTypedefElement = targetDoc->GetElementById(typedefId);
215                         if (targetTypedefElement)
216                         {
217                             std::u32string targetAttr = targetTypedefElement->GetAttribute(U"target");
218                             if (targetAttr.empty())
219                             {
220                                 elementsToRemove.push_back(targetTypedefElement);
221                                 elementsToAdd.push_back(typedefElement);
222                             }
223                         }
224                         else
225                         {
226                             elementsToAdd.push_back(typedefElement);
227                         }
228                     }
229                 }
230             }
231         }
232     }
233     for (sngxml::dom::Element* elementToRemove : elementsToRemove)
234     {
235         RemoveElement(elementToRemove);
236     }
237     for (sngxml::dom::Element* elementToAdd : elementsToAdd)
238     {
239         AddElement(elementToAddnullptrtargetDoctrueU"typedefs");
240     }
241 }
242 
243 void MergeClasses(sngxml::dom::Element* sourceElementsngxml::dom::Document* targetDoc)
244 {
245     std::vector<sngxml::dom::Element*> elementsToRemove;
246     std::vector<sngxml::dom::Element*> elementsToAdd;
247     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"classes/class"sourceElement);
248     if (result)
249     {
250         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
251         {
252             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
253             int n = nodeSet->Length();
254             for (int i = 0; i < n; ++i)
255             {
256                 sngxml::dom::Node* node = (*nodeSet)[i];
257                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
258                 {
259                     sngxml::dom::Element* classElement = static_cast<sngxml::dom::Element*>(node);
260                     std::u32string classId = classElement->GetAttribute(U"id");
261                     if (!classId.empty())
262                     {
263                         sngxml::dom::Element* targetClassElement = targetDoc->GetElementById(classId);
264                         if (targetClassElement)
265                         {
266                             std::u32string targetAttr = targetClassElement->GetAttribute(U"target");
267                             if (targetAttr.empty())
268                             {
269                                 elementsToRemove.push_back(targetClassElement);
270                                 elementsToAdd.push_back(classElement);
271                             }
272                         }
273                         else
274                         {
275                             elementsToAdd.push_back(classElement);
276                         }
277                     }
278                     MergeClasses(classElementtargetDoc);
279                     MergeConstructors(classElementtargetDoc);
280                     MergeFunctions(classElementtargetDoc);
281                     MergeTypedefs(classElementtargetDoc);
282                 }
283             }
284         }
285     }
286     for (sngxml::dom::Element* elementToRemove : elementsToRemove)
287     {
288         RemoveElement(elementToRemove);
289     }
290     for (sngxml::dom::Element* elementToAdd : elementsToAdd)
291     {
292         AddElement(elementToAddnullptrtargetDoctrueU"classes");
293     }
294 }
295 
296 void MergeTypes(sngxml::dom::Element* sourceElementsngxml::dom::Document* targetDoc)
297 {
298     std::vector<sngxml::dom::Element*> elementsToRemove;
299     std::vector<sngxml::dom::Element*> elementsToAdd;
300     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"types"sourceElement);
301     if (result)
302     {
303         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
304         {
305             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
306             int n = nodeSet->Length();
307             if (n == 1)
308             {
309                 sngxml::dom::Node* node = (*nodeSet)[0];
310                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
311                 {
312                     sngxml::dom::Element* typesElement = static_cast<sngxml::dom::Element*>(node);
313                     sngxml::dom::Node* typeNode = typesElement->FirstChild();
314                     while (typeNode)
315                     {
316                         if (typeNode->GetNodeType() == sngxml::dom::NodeType::elementNode)
317                         {
318                             sngxml::dom::Element* typeElement = static_cast<sngxml::dom::Element*>(typeNode);
319                             std::u32string typeId = typeElement->GetAttribute(U"id");
320                             if (!typeId.empty())
321                             {
322                                 sngxml::dom::Element* targetTypeElement = targetDoc->GetElementById(typeId);
323                                 if (targetTypeElement)
324                                 {
325                                     std::u32string targetAttr = targetTypeElement->GetAttribute(U"target");
326                                     if (targetAttr.empty())
327                                     {
328                                         elementsToRemove.push_back(targetTypeElement);
329                                         elementsToAdd.push_back(typeElement);
330                                     }
331                                 }
332                                 else
333                                 {
334                                     elementsToAdd.push_back(typeElement);
335                                 }
336                             }
337                         }
338                         typeNode = typeNode->NextSibling();
339                     }
340                 }
341             }
342         }
343     }
344     for (sngxml::dom::Element* elementToRemove : elementsToRemove)
345     {
346         RemoveElement(elementToRemove);
347     }
348     std::unique_ptr<sngxml::xpath::XPathObject> targetResult = sngxml::xpath::Evaluate(U"types"targetDoc->DocumentElement());
349     if (targetResult)
350     {
351         if (targetResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
352         {
353             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(targetResult.get());
354             int n = nodeSet->Length();
355             if (n == 1)
356             {
357                 sngxml::dom::Node* node = (*nodeSet)[0];
358                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
359                 {
360                     sngxml::dom::Element* typesElement = static_cast<sngxml::dom::Element*>(node);
361                     for (sngxml::dom::Element* elementToAdd : elementsToAdd)
362                     {
363                         typesElement->AppendChild(elementToAdd->CloneNode(true));
364                     }
365                 }
366             }
367         }
368     }
369 }
370 
371 std::std::unique_ptr<sngxml::dom::Document>Merge(sngxml::dom::Document*symbolTableXmlDocstd::std::unique_ptr<sngxml::dom::Document>&stageSymbolTableXmlDoc)
372 {
373     std::unique_ptr<sngxml::xpath::XPathObject> ns = sngxml::xpath::Evaluate(U"//namespace"symbolTableXmlDoc);
374     if (ns)
375     {
376         if (ns->Type() == sngxml::xpath::XPathObjectType::nodeSet)
377         {
378             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(ns.get());
379             int n = nodeSet->Length();
380             for (int i = 0; i < n; ++i)
381             {
382                 sngxml::dom::Node* node = (*nodeSet)[i];
383                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
384                 {
385                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
386                     MergeClasses(elementstageSymbolTableXmlDoc.get());
387                     MergeFunctions(elementstageSymbolTableXmlDoc.get());
388                     MergeTypedefs(elementstageSymbolTableXmlDoc.get());
389                 }
390             }
391         }
392     }
393     MergeTypes(symbolTableXmlDoc->DocumentElement()stageSymbolTableXmlDoc.get());
394     std::unique_ptr<sngxml::dom::Document> result;
395     result = std::move(stageSymbolTableXmlDoc);
396     return result;
397 }
398 
399 } // namespace cpp2cm
400