53a8f8010da2130551c06b8d2aa7563ab296f85e
[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     switch(xmlNode->type) {
129     case 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       break;
142     case 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
152         w ++;
153       }
154
155       if(isSeparator == false)
156         node->createText(decode(xmlNode->content));
157
158       break;
159     }
160
161     xmlNode = xmlNode->next;
162   }
163 }
164
165 void Parser::attributes(Node* node, xmlAttr* attribute)
166 throw(RuntimeException) {
167   const char* value;
168   const Namespace* ns = NULL;
169
170   while(attribute != NULL) {
171     ns = (attribute->ns == NULL) ? NULL :  node->namespace_find((const char*) attribute->ns->prefix);
172     value = (attribute->children != NULL) ? decode(attribute->children->content) : "";
173     node->createAttribute((const char*) attribute->name, value, ns);
174     attribute = attribute->next;
175   }
176 }
177
178 /* static */
179 void Parser::errorHandler(void *ctx,  const char *msg, ...)
180 throw() {
181   va_list ap;
182   va_start(ap, msg);
183   vsprintf(st_text, msg, ap);
184
185   for(char* aux = st_text; *aux; aux ++)
186     if(*aux == '\n')
187       *aux = ' ';
188
189   va_end(ap);
190   Logger::write(Logger::Error, st_text, ANNA_FILE_LOCATION);
191 }
192
193 /* static */
194 void Parser::warningHandler(void *ctx,  const char *msg, ...)
195 throw() {
196   va_list ap;
197
198   if(Logger::isActive(Logger::Warning)) {
199     va_start(ap, msg);
200     vsprintf(st_text, msg, ap);
201
202     for(char* aux = st_text; *aux; aux ++)
203       if(*aux == '\n')
204         *aux = ' ';
205
206     va_end(ap);
207     Logger::write(Logger::Warning, st_text, ANNA_FILE_LOCATION);
208   }
209 }
210
211 // La metodo que invoque a este debe hacerlo con una secci�n cr�tica activa.
212 void Parser::reset()
213 throw() {
214   if(a_root != NULL)
215     a_root->clear();
216 }
217
218 void Parser::setupEncoding(_xmlDoc* document)
219 throw() {
220   if(document->encoding != NULL) {
221     a_encoding = true;
222
223     if(a_buffEncode == NULL)
224       a_buffEncode = new DataBlock(true);
225     else
226       a_buffEncode->clear();
227   } else
228     a_encoding = false;
229
230   LOGDEBUG(
231     string msg("anna::xml::Parser::setupEncoding | Encoding: ");
232     msg += (document->encoding == NULL) ? "none" : (const char*) document->encoding;
233     Logger::debug(msg, ANNA_FILE_LOCATION);
234   );
235 }
236
237 const char* Parser::decode(const unsigned char* source)
238 throw() {
239   if(a_encoding == false)
240     return (const char*) source;
241
242   if(xmlCheckUTF8(source) == 0)
243     return (const char*) source;
244
245   int srcLen = xmlStrlen(source);
246   a_buffEncode->clear();
247   a_buffEncode->allocate(srcLen + 1);
248   unsigned char* target = (unsigned char*) a_buffEncode->getData();
249   int trgLen = srcLen;
250   UTF8Toisolat1(target, &trgLen, source, &srcLen);
251   target [trgLen] = 0;
252   return (const char*) target;
253 }