Suuports clang compiler
[anna.git] / source / xml / Parser.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // http://redmine.teslayout.com/projects/anna-suite
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     *  Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <ctype.h>
38
39 #include <iostream>
40
41 #include <stdarg.h>
42
43 #include <libxml/parser.h>
44
45 #include <anna/core/functions.hpp>
46 #include <anna/core/tracing/Logger.hpp>
47 #include <anna/core/tracing/TraceMethod.hpp>
48 #include <anna/core/tracing/TraceFunction.hpp>
49 #include <anna/core/mt/Guard.hpp>
50
51 #include <anna/xml/xml.hpp>
52 #include <anna/xml/internal/sccs.hpp>
53
54 using namespace std;
55 using namespace anna;
56
57 char Parser::st_text [1024] = "";
58 RuntimeException* Parser::st_exception = NULL;
59
60 Parser::Parser() :
61   a_root(NULL),
62   a_encoding(false),
63   a_buffEncode(NULL) {
64   sccs::activate();
65   a_context = (xmlValidCtxtPtr) xmlMalloc(sizeof(xmlValidCtxt));
66   a_context->error = errorHandler;
67   a_context->warning = warningHandler;
68 }
69
70 Parser::~Parser() {
71   reset();
72   xmlFree(a_context);
73   delete a_buffEncode;
74   delete a_root;
75 }
76
77 const Node* Parser::apply(const Document& document)
78 throw(RuntimeException) {
79   LOGMETHOD(TraceMethod tf("anna::xml::Parser", "apply(Document)", ANNA_FILE_LOCATION));
80   Guard guard(*this);
81   reset();
82   apply(const_cast <_xmlDoc*>(document.a_handle));
83   return a_root;
84 }
85
86 const Node* Parser::apply(const Document& document, const DTD& dtd)
87 throw(RuntimeException) {
88   LOGMETHOD(TraceMethod tf("anna::xml::Parser", "apply(Document,DTD)", ANNA_FILE_LOCATION));
89   Guard guard(*this);
90   reset();
91   dtd.validate(a_context, const_cast <_xmlDoc*>(document.a_handle));
92   apply(const_cast <_xmlDoc*>(document.a_handle));
93   return a_root;
94 }
95
96 void Parser::apply(_xmlDoc* document)
97 throw(RuntimeException) {
98   LOGMETHOD(TraceMethod tf("anna::xml::Parser", "apply (_xmlDoc)", ANNA_FILE_LOCATION));
99   xmlNode* root;
100
101   if((root = xmlDocGetRootElement(document)) == NULL)
102     throw RuntimeException("Error interpreting XML document", ANNA_FILE_LOCATION);
103
104   setupEncoding(document);
105
106   if(a_root == NULL)
107     a_root = new Node((const char*) root->name);
108   else
109     a_root->setName((const char*) root->name);
110
111   for(xmlNs* ns = root->nsDef; ns != NULL; ns = ns->next)
112     a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
113
114   if(root->ns)
115     a_root->setNamespace(a_root->namespace_find((const char*) root->ns->prefix));
116
117   attributes(a_root, root->properties);
118   children(a_root, root->children);
119 }
120
121 void Parser::children(Node* node, xmlNode* xmlNode)
122 throw(RuntimeException) {
123   Node* child;
124   bool isSeparator;
125   const char* w;
126
127   while(xmlNode != NULL) {
128
129     if((xmlNode->type) == XML_ELEMENT_NODE) {
130       child  = node->createChild((const char*) xmlNode->name);
131
132       for(xmlNs* ns = xmlNode->nsDef; ns != NULL; ns = ns->next)
133         if(ns->prefix != NULL)
134           a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
135
136       if(xmlNode->ns != NULL && xmlNode->ns->prefix != NULL)
137         child->setNamespace(node->namespace_find((const char*) xmlNode->ns->prefix));
138
139       attributes(child, xmlNode->properties);
140       children(child, xmlNode->children);
141     }
142     else if((xmlNode->type) == XML_TEXT_NODE) {
143       w = (const char*) xmlNode->content;
144       isSeparator = true;
145
146       while(*w != 0) {
147         if(isspace(*w) == false) {
148           isSeparator = false;
149           break;
150         }
151         w ++;
152       }
153
154       if(isSeparator == false)
155         node->createText(decode(xmlNode->content));
156     }
157
158
159     xmlNode = xmlNode->next;
160   }
161 }
162
163 void Parser::attributes(Node* node, xmlAttr* attribute)
164 throw(RuntimeException) {
165   const char* value;
166   const Namespace* ns = NULL;
167
168   while(attribute != NULL) {
169     ns = (attribute->ns == NULL) ? NULL :  node->namespace_find((const char*) attribute->ns->prefix);
170     value = (attribute->children != NULL) ? decode(attribute->children->content) : "";
171     node->createAttribute((const char*) attribute->name, value, ns);
172     attribute = attribute->next;
173   }
174 }
175
176 /* static */
177 void Parser::errorHandler(void *ctx,  const char *msg, ...)
178 throw() {
179   va_list ap;
180   va_start(ap, msg);
181   vsprintf(st_text, msg, ap);
182
183   for(char* aux = st_text; *aux; aux ++)
184     if(*aux == '\n')
185       *aux = ' ';
186
187   va_end(ap);
188   Logger::write(Logger::Error, st_text, ANNA_FILE_LOCATION);
189 }
190
191 /* static */
192 void Parser::warningHandler(void *ctx,  const char *msg, ...)
193 throw() {
194   va_list ap;
195
196   if(Logger::isActive(Logger::Warning)) {
197     va_start(ap, msg);
198     vsprintf(st_text, msg, ap);
199
200     for(char* aux = st_text; *aux; aux ++)
201       if(*aux == '\n')
202         *aux = ' ';
203
204     va_end(ap);
205     Logger::write(Logger::Warning, st_text, ANNA_FILE_LOCATION);
206   }
207 }
208
209 // La metodo que invoque a este debe hacerlo con una secci�n cr�tica activa.
210 void Parser::reset()
211 throw() {
212   if(a_root != NULL)
213     a_root->clear();
214 }
215
216 void Parser::setupEncoding(_xmlDoc* document)
217 throw() {
218   if(document->encoding != NULL) {
219     a_encoding = true;
220
221     if(a_buffEncode == NULL)
222       a_buffEncode = new DataBlock(true);
223     else
224       a_buffEncode->clear();
225   } else
226     a_encoding = false;
227
228   LOGDEBUG(
229     string msg("anna::xml::Parser::setupEncoding | Encoding: ");
230     msg += (document->encoding == NULL) ? "none" : (const char*) document->encoding;
231     Logger::debug(msg, ANNA_FILE_LOCATION);
232   );
233 }
234
235 const char* Parser::decode(const unsigned char* source)
236 throw() {
237   if(a_encoding == false)
238     return (const char*) source;
239
240   if(xmlCheckUTF8(source) == 0)
241     return (const char*) source;
242
243   int srcLen = xmlStrlen(source);
244   a_buffEncode->clear();
245   a_buffEncode->allocate(srcLen + 1);
246   unsigned char* target = (unsigned char*) a_buffEncode->getData();
247   int trgLen = srcLen;
248   UTF8Toisolat1(target, &trgLen, source, &srcLen);
249   target [trgLen] = 0;
250   return (const char*) target;
251 }