Normalize xml processing
[anna.git] / source / xml / Compiler.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <anna/core/mt/Guard.hpp>
10 #include <anna/core/util/defines.hpp>
11
12 #include <anna/xml/Compiler.hpp>
13 #include <anna/xml/Node.hpp>
14 #include <anna/xml/Attribute.hpp>
15 #include <anna/xml/Text.hpp>
16 #include <anna/xml/internal/sccs.hpp>
17 #include <anna/xml/Document.hpp>
18 #include <anna/xml/Namespace.hpp>
19
20 #include <algorithm>
21 #include <cstring>
22
23
24 using namespace anna;
25 using namespace anna::xml;
26
27 Compiler::Compiler() {
28   sccs::activate();
29 }
30
31 const char* Compiler::apply(const Node* node, const int flags)
32 throw(RuntimeException) {
33   Guard guard(this, "xml::Compiler::apply");
34   a_result.clear();
35
36   if(flags & Mode::Compact)
37     apply(node, a_result, flags);
38   else
39     apply(node, a_result, 0, flags);
40
41   return a_result.c_str();
42 }
43
44 const char* Compiler::apply(const Document& document, const Node* node, const int flags)
45 throw(RuntimeException) {
46   Guard guard(this, "xml::Compiler::apply");
47   a_result.clear();
48   const char* version = document.getVersion();
49   a_result += "<?xml";
50
51   if(version != NULL) {
52     a_result += " version='";
53     a_result += version;
54     a_result += "'";
55   }
56
57   const char* encoding = document.getEncoding();
58
59   if(encoding != NULL) {
60     a_result += " encoding='";
61     a_result += encoding;
62     a_result += "'";
63   }
64
65   a_result += "?>\n";
66
67   if(flags & Mode::Compact)
68     apply(node, a_result, flags);
69   else
70     apply(node, a_result, 0, flags);
71
72   return a_result.c_str();
73 }
74
75 //------------------------------------------------------------------------------------
76 // (1) Si tiene nodos hijos => ignora el texto, porque no se como representar
77 // nodos + texto.
78 //------------------------------------------------------------------------------------
79 void Compiler::apply(const Node* node, Result& result, const int level, const int flags)
80 throw(RuntimeException) {
81   const bool hasText(node->getText() != NULL);
82   Node::const_child_iterator ii = node->child_begin();
83   Node::const_child_iterator maxii = node->child_end();
84   const bool hasChildren(ii != maxii);
85
86   if(hasChildren == false && hasText == false)
87     open(node, result, level, true, true, flags);
88   else if(hasChildren == false && hasText == true) {
89     open(node, result, level, false, false, flags);
90     result += node->getText()->getValue();
91     close(node, result, 0, flags);
92   } else if(hasChildren == true) {                    // (1)
93     open(node, result, level, false, true, flags);
94
95     for(; ii != maxii; ii ++)
96       apply(Node::node(ii), result, level + 1, flags);
97
98     close(node, result, level, flags);
99   }
100 }
101
102 //------------------------------------------------------------------------------------
103 // (1) Si tiene nodos hijos => ignora el texto, porque no se como representar
104 // nodos + texto.
105 //------------------------------------------------------------------------------------
106 void Compiler::apply(const Node* node, Result& result, const int flags)
107 throw(RuntimeException) {
108   static const int level = 0;
109   const bool hasText(node->getText() != NULL);
110   Node::const_child_iterator ii = node->child_begin();
111   Node::const_child_iterator maxii = node->child_end();
112   const bool hasChildren(ii != maxii);
113
114   if(hasChildren == false && hasText == false)
115     open(node, result, level, true, false, flags);
116   else if(hasChildren == false && hasText == true) {
117     open(node, result, level, false, false, flags);
118     result += node->getText()->getValue();
119     result += "</";
120     writeFullName(node, result, flags);
121     result += ">";
122   } else if(hasChildren == true) {                    // (1)
123     open(node, result, level, false, false, flags);
124
125     for(; ii != maxii; ii ++)
126       apply(Node::node(ii), result, flags);
127
128     result += "</";
129     writeFullName(node, result, flags);
130     result += ">";
131   }
132 }
133
134 /*static*/
135 void Compiler::open(const Node* node, Result& result, const int level, const bool quickClose, const bool newline, const int flags)
136 throw(RuntimeException) {
137   const Attribute* attribute;
138   const Namespace* ns;
139
140   for(int i = 0; i < level; i ++)
141     result += std::string(ANNA_XML_INDENTATION_SPACES, ' ');
142
143   result += '<';
144   writeFullName(node, result, flags);
145
146   // Si es el nodo ROOT => Tenemos que representar la deficiĆ³n de los namespaces
147   if(node->getParent() == NULL && node->namespace_size() != 0) {
148     for(xml::Node::const_namespace_iterator ii = node->namespace_begin(), maxii = node->namespace_end(); ii != maxii; ii ++) {
149       ns = xml::Node::xnamespace(ii);
150       result += " xmlns:";
151       result += ns->getName();
152       result += "=\"";
153       result += ns->getReference();
154       result += "\"";
155     }
156   }
157
158   // Sort node attributes (this is a compiler used for on-demand representation, this sorting is not permanent in the object which uses it):
159   if(flags & Mode::Sort) {
160     Node *nc_node = const_cast<Node*>(node);
161     std::sort(nc_node->attribute_begin(), nc_node->attribute_end(),
162               [](Attribute *a, Attribute *b) { return (std::strcmp(a->getName(), b->getName()) < 0); }); // sort alphabetically by attribute name
163   }
164
165   for(Node::const_attribute_iterator ii = node->attribute_begin(), maxii = node->attribute_end(); ii != maxii; ii ++) {
166     attribute = Node::attribute(ii);
167     result += ' ';
168     writeFullName(attribute, result, flags);
169     const std::string& value = attribute->getValue();
170
171     if(value.length() > 0) {
172       result += "=\"";
173       result += attribute->getValue();
174       result += "\"";
175     } else if(flags & Mode::ShowNullAttribute)
176       result += "=\"\"";
177   }
178
179   if(quickClose == true)
180     result += '/';
181
182   result += '>';
183
184   if(newline == true)
185     result += '\n';
186 }
187
188 /*static*/
189 void Compiler::writeFullName(const Node* node, Result& result, const int flags)
190 throw(RuntimeException) {
191   const Namespace* ns;
192
193   if((flags & Mode::NoNamespaces) == 0 && (ns = node->getNamespace()) != NULL) {
194     result += ns->getName();
195     result += ':';
196   }
197
198   result += node->getName();
199 }
200
201 /*static*/
202 void Compiler::writeFullName(const Attribute* attr, Result& result, const int flags)
203 throw(RuntimeException) {
204   const Namespace* ns;
205
206   if((flags & Mode::NoNamespaces) == 0 && (ns = attr->getNamespace()) != NULL) {
207     result += ns->getName();
208     result += ':';
209   }
210
211   result += attr->getName();
212 }
213
214 /*static*/
215 void Compiler::close(const Node* node, Result& result, const int level, const int flags)
216 throw(RuntimeException) {
217   for(int i = 0; i < level; i ++)
218     result += std::string(ANNA_XML_INDENTATION_SPACES, ' ');
219
220   result += "</";
221   writeFullName(node, result, flags);
222   result += ">\n";
223 }
224
225 void Compiler::Result::extend(const int nbytes)
226 throw(RuntimeException) {
227   if((a_size + nbytes) >= a_maxSize) {
228     int newSize = ((a_size + nbytes) << 1) - ((a_size + nbytes)  >> 1);
229     char* newBuffer = new char [newSize];
230
231     if(newBuffer == NULL)
232       throw RuntimeException("Insufficient memory", ANNA_FILE_LOCATION);
233
234     if(a_size > 0)
235       anna_memcpy(newBuffer, a_buffer, a_size);
236
237     delete [] a_buffer;
238     a_buffer = newBuffer;
239     a_maxSize = newSize;
240   }
241 }
242