1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
39 #include <libxml/xpath.h>
40 #include <libxml/xpathInternals.h>
41 #include <libxml/parser.h>
43 #include <anna/core/functions.hpp>
44 #include <anna/core/tracing/Logger.hpp>
45 #include <anna/core/tracing/TraceMethod.hpp>
46 #include <anna/core/tracing/TraceFunction.hpp>
48 #include <anna/xml/XPath.hpp>
49 #include <anna/xml/internal/sccs.hpp>
50 #include <anna/xml/Document.hpp>
55 xml::XPath::XPath(const char* name) {
56 a_root = new Node(name);
61 * Según la implementación de
62 * http://www.xmlsoft.org/examples/xpath1.c
64 * Los nodos se recuperan mediante los métodos node_begin, node_end y node
66 void xml::XPath::apply(const Document& document, const char* expression, const int mode)
67 throw(RuntimeException) {
69 ConfigSkeleton configSkeleton(&XPath::callbackApply);
71 if((mode & Mode::Namespace) != 0)
72 configSkeleton.initialize = &XPath::initializeNamespaces;
74 skeleton(document, expression, configSkeleton);
77 bool xml::XPath::match(const Document& document, const char* expression, const int mode)
78 throw(RuntimeException) {
80 ConfigSkeleton configSkeleton(&XPath::callbackMatch);
82 if((mode & Mode::Namespace) != 0)
83 configSkeleton.initialize = &XPath::initializeNamespaces;
85 skeleton(document, expression, configSkeleton);
87 string msg("xml::XPath::match | Expression: ");
89 msg += anna::functions::asText(" | Result: ", a_result);
90 Logger::debug(msg, ANNA_FILE_LOCATION);
95 const xml::Node* xml::XPath::find(const Document& document, const char* expression, const int mode, const Exception::Mode::_v emode)
96 throw(RuntimeException) {
97 apply(document, expression, mode);
98 const xml::Node* result = NULL;
99 Node::const_child_iterator ii = node_begin();
101 if(ii == node_end()) {
102 if(emode == Exception::Mode::Throw) {
103 string msg("xml::XPath::find | Expression: ");
105 msg += " | Does not match any xml::Node";
106 throw RuntimeException(msg, ANNA_FILE_LOCATION);
107 } else if(emode == Exception::Mode::Trace) {
108 string msg("xml::XPath::find | Expression: ");
110 msg += " | Does not match any xml::Node";
111 Logger::warning(msg, ANNA_FILE_LOCATION);
118 string msg("xml::XPath::find | Expression: ");
120 msg += functions::asString(" | Ignores %d xml::Node's", size());
121 Logger::warning(msg, ANNA_FILE_LOCATION);
124 return XPath::node(ii);
127 void xml::XPath::skeleton(const Document& document, const char* expression, xml::XPath::ConfigSkeleton& config)
128 throw(RuntimeException) {
130 string msg("xml::XPath::skeleton | Document: ");
131 msg += functions::asString(document.getContent());
132 msg += " | Expression: ";
134 Logger::debug(msg, ANNA_FILE_LOCATION);
137 if(document.a_handle == NULL)
138 throw RuntimeException("xml::XPath::pattern | xml::Document had not been initialized", ANNA_FILE_LOCATION);
140 setupEncoding(document.a_handle);
142 xmlXPathContextPtr context = NULL;
143 xmlXPathObjectPtr object = NULL;
146 if((context = xmlXPathNewContext(document.a_handle)) == NULL)
147 throw RuntimeException("Can not create XPath context", ANNA_FILE_LOCATION);
149 if(config.initialize != NULL)
150 (this->*config.initialize)(context, document);
152 if((object = xmlXPathEvalExpression(BAD_CAST expression, context)) == NULL) {
153 string msg("xml::XPath::skeleton | Expression: ");
155 msg += " | Unable to evaluate expression";
156 throw RuntimeException(msg, ANNA_FILE_LOCATION);
159 (this->*config.callback)(object->nodesetval);
160 xmlXPathFreeObject(object);
161 xmlXPathFreeContext(context);
162 } catch(RuntimeException& ex) {
164 xmlXPathFreeObject(object);
167 xmlXPathFreeContext(context);
174 * Ha que tener en cuenta que puede haber definiciones de NS en cualquier nodo
175 * lo que nos obliga a recorrerlo en profundidad.
177 void xml::XPath::initializeNamespaces(_xmlXPathContext* context, const Document& document)
178 throw(RuntimeException) {
181 if((root = xmlDocGetRootElement(document.a_handle)) == NULL)
182 throw RuntimeException("Error interpreting the XML document", ANNA_FILE_LOCATION);
184 // Incorpora la definición de los namespaces usados en el documento y los define en el contexto XPath
185 forwardNamespaces(context, root);
188 void xml::XPath::forwardNamespaces(_xmlXPathContext* context, _xmlNode* xmlNode)
189 throw(RuntimeException) {
190 while(xmlNode != NULL) {
191 if(xmlNode->type == XML_ELEMENT_NODE) {
192 for(xmlNs* ns = xmlNode->nsDef; ns != NULL; ns = ns->next) {
193 a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
194 xmlXPathRegisterNs(context, ns->prefix, ns->href);
196 string msg("xml::XPath::forwardNamespaces | Prefix: ");
197 msg += (const char*) ns->prefix;
199 msg += (const char*) ns->href;
200 Logger::debug(msg, ANNA_FILE_LOCATION);
204 forwardNamespaces(context, xmlNode->children);
207 xmlNode = xmlNode->next;
211 void xml::XPath::callbackApply(const _xmlNodeSet* nodes) {
212 /* Si no hay nodos que cumplan con la expresión */
219 for(int ii = 0, maxii = nodes->nodeNr; ii < maxii; ii ++) {
220 xmlNode = nodes->nodeTab [ii];
222 if(xmlNode->type != XML_ELEMENT_NODE)
225 /* Sólo analiza el primer nivel de dependencia de cada uno de los nodos seleccionados */
226 node = a_root->createChild((const char*) xmlNode->name);
227 attributes(node, xmlNode->properties);
229 if(xmlNode->ns != NULL)
230 node->setNamespace(a_root->namespace_find((const char*) xmlNode->ns->prefix));
232 if((a_mode & Mode::Full) != 0)
233 children(node, xmlNode->children);
235 text(node, xmlNode->children);
237 LOGDEBUG(Logger::debug(node->asString(), ANNA_FILE_LOCATION));
241 void xml::XPath::callbackMatch(const _xmlNodeSet* nodes) {
242 a_result = (nodes == NULL) ? false : (nodes->nodeNr > 0);
245 void xml::XPath::text(Node* node, xmlNode* xmlNode)
246 throw(RuntimeException) {
250 while(xmlNode != NULL) {
251 switch(xmlNode->type) {
253 w = (const char*) xmlNode->content;
257 if(isspace(*w) == false) {
265 if(isSeparator == false)
266 node->createText(decode(xmlNode->content));
271 xmlNode = xmlNode->next;