Ensures normalization on waitfe/fc-xml operations
[anna.git] / source / xml / Parser.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 <ctype.h>
10
11 #include <iostream>
12
13 #include <stdarg.h>
14
15 #include <libxml/parser.h>
16
17 #include <anna/core/functions.hpp>
18 #include <anna/core/tracing/Logger.hpp>
19 #include <anna/core/tracing/TraceMethod.hpp>
20 #include <anna/core/tracing/TraceFunction.hpp>
21 #include <anna/core/mt/Guard.hpp>
22
23 #include <anna/xml/xml.hpp>
24 #include <anna/xml/internal/sccs.hpp>
25
26 using namespace std;
27 using namespace anna;
28
29 char Parser::st_text [1024] = "";
30 RuntimeException* Parser::st_exception = NULL;
31
32 Parser::Parser() :
33   a_root(NULL),
34   a_encoding(false),
35   a_buffEncode(NULL) {
36   sccs::activate();
37   a_context = (xmlValidCtxtPtr) xmlMalloc(sizeof(xmlValidCtxt));
38   a_context->error = errorHandler;
39   a_context->warning = warningHandler;
40 }
41
42 Parser::~Parser() {
43   reset();
44   xmlFree(a_context);
45   delete a_buffEncode;
46   delete a_root;
47 }
48
49 const Node* Parser::apply(const Document& document)
50 throw(RuntimeException) {
51   LOGMETHOD(TraceMethod tf("anna::xml::Parser", "apply(Document)", ANNA_FILE_LOCATION));
52   Guard guard(*this);
53   reset();
54   apply(const_cast <_xmlDoc*>(document.a_handle));
55   return a_root;
56 }
57
58 const Node* Parser::apply(const Document& document, const DTD& dtd)
59 throw(RuntimeException) {
60   LOGMETHOD(TraceMethod tf("anna::xml::Parser", "apply(Document,DTD)", ANNA_FILE_LOCATION));
61   Guard guard(*this);
62   reset();
63   dtd.validate(a_context, const_cast <_xmlDoc*>(document.a_handle));
64   apply(const_cast <_xmlDoc*>(document.a_handle));
65   return a_root;
66 }
67
68 void Parser::apply(_xmlDoc* document)
69 throw(RuntimeException) {
70   LOGMETHOD(TraceMethod tf("anna::xml::Parser", "apply (_xmlDoc)", ANNA_FILE_LOCATION));
71   xmlNode* root;
72
73   if((root = xmlDocGetRootElement(document)) == NULL)
74     throw RuntimeException("Error interpreting XML document", ANNA_FILE_LOCATION);
75
76   setupEncoding(document);
77
78   if(a_root == NULL)
79     a_root = new Node((const char*) root->name);
80   else
81     a_root->setName((const char*) root->name);
82
83   for(xmlNs* ns = root->nsDef; ns != NULL; ns = ns->next)
84     a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
85
86   if(root->ns)
87     a_root->setNamespace(a_root->namespace_find((const char*) root->ns->prefix));
88
89   attributes(a_root, root->properties);
90   children(a_root, root->children);
91 }
92
93 void Parser::children(Node* node, xmlNode* xmlNode)
94 throw(RuntimeException) {
95   Node* child;
96   bool isSeparator;
97   const char* w;
98
99   while(xmlNode != NULL) {
100
101     if((xmlNode->type) == XML_ELEMENT_NODE) {
102       child  = node->createChild((const char*) xmlNode->name);
103
104       for(xmlNs* ns = xmlNode->nsDef; ns != NULL; ns = ns->next)
105         if(ns->prefix != NULL)
106           a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
107
108       if(xmlNode->ns != NULL && xmlNode->ns->prefix != NULL)
109         child->setNamespace(node->namespace_find((const char*) xmlNode->ns->prefix));
110
111       attributes(child, xmlNode->properties);
112       children(child, xmlNode->children);
113     }
114     else if((xmlNode->type) == XML_TEXT_NODE) {
115       w = (const char*) xmlNode->content;
116       isSeparator = true;
117
118       while(*w != 0) {
119         if(isspace(*w) == false) {
120           isSeparator = false;
121           break;
122         }
123         w ++;
124       }
125
126       if(isSeparator == false)
127         node->createText(decode(xmlNode->content));
128     }
129
130
131     xmlNode = xmlNode->next;
132   }
133 }
134
135 void Parser::attributes(Node* node, xmlAttr* attribute)
136 throw(RuntimeException) {
137   const char* value;
138   const Namespace* ns = NULL;
139
140   while(attribute != NULL) {
141     ns = (attribute->ns == NULL) ? NULL :  node->namespace_find((const char*) attribute->ns->prefix);
142     value = (attribute->children != NULL) ? decode(attribute->children->content) : "";
143     node->createAttribute((const char*) attribute->name, value, ns);
144     attribute = attribute->next;
145   }
146 }
147
148 /* static */
149 void Parser::errorHandler(void *ctx,  const char *msg, ...)
150 throw() {
151   va_list ap;
152   va_start(ap, msg);
153   vsprintf(st_text, msg, ap);
154
155   for(char* aux = st_text; *aux; aux ++)
156     if(*aux == '\n')
157       *aux = ' ';
158
159   va_end(ap);
160   Logger::write(Logger::Error, st_text, ANNA_FILE_LOCATION);
161 }
162
163 /* static */
164 void Parser::warningHandler(void *ctx,  const char *msg, ...)
165 throw() {
166   va_list ap;
167
168   if(Logger::isActive(Logger::Warning)) {
169     va_start(ap, msg);
170     vsprintf(st_text, msg, ap);
171
172     for(char* aux = st_text; *aux; aux ++)
173       if(*aux == '\n')
174         *aux = ' ';
175
176     va_end(ap);
177     Logger::write(Logger::Warning, st_text, ANNA_FILE_LOCATION);
178   }
179 }
180
181 // La metodo que invoque a este debe hacerlo con una secci�n cr�tica activa.
182 void Parser::reset()
183 throw() {
184   if(a_root != NULL)
185     a_root->clear();
186 }
187
188 void Parser::setupEncoding(_xmlDoc* document)
189 throw() {
190   if(document->encoding != NULL) {
191     a_encoding = true;
192
193     if(a_buffEncode == NULL)
194       a_buffEncode = new DataBlock(true);
195     else
196       a_buffEncode->clear();
197   } else
198     a_encoding = false;
199
200   LOGDEBUG(
201     string msg("anna::xml::Parser::setupEncoding | Encoding: ");
202     msg += (document->encoding == NULL) ? "none" : (const char*) document->encoding;
203     Logger::debug(msg, ANNA_FILE_LOCATION);
204   );
205 }
206
207 const char* Parser::decode(const unsigned char* source)
208 throw() {
209   if(a_encoding == false)
210     return (const char*) source;
211
212   if(xmlCheckUTF8(source) == 0)
213     return (const char*) source;
214
215   int srcLen = xmlStrlen(source);
216   a_buffEncode->clear();
217   a_buffEncode->allocate(srcLen + 1);
218   unsigned char* target = (unsigned char*) a_buffEncode->getData();
219   int trgLen = srcLen;
220   UTF8Toisolat1(target, &trgLen, source, &srcLen);
221   target [trgLen] = 0;
222   return (const char*) target;
223 }