1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
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 //
11 #include <libxml/xpath.h>
12 #include <libxml/xpathInternals.h>
13 #include <libxml/parser.h>
15 #include <anna/core/functions.hpp>
16 #include <anna/core/tracing/Logger.hpp>
17 #include <anna/core/tracing/TraceMethod.hpp>
18 #include <anna/core/tracing/TraceFunction.hpp>
20 #include <anna/xml/XPath.hpp>
21 #include <anna/xml/internal/sccs.hpp>
22 #include <anna/xml/Document.hpp>
27 xml::XPath::XPath(const char* name) {
28 a_root = new Node(name);
33 * Según la implementación de
34 * http://www.xmlsoft.org/examples/xpath1.c
36 * Los nodos se recuperan mediante los métodos node_begin, node_end y node
38 void xml::XPath::apply(const Document& document, const char* expression, const int mode)
39 throw(RuntimeException) {
41 ConfigSkeleton configSkeleton(&XPath::callbackApply);
43 if((mode & Mode::Namespace) != 0)
44 configSkeleton.initialize = &XPath::initializeNamespaces;
46 skeleton(document, expression, configSkeleton);
49 bool xml::XPath::match(const Document& document, const char* expression, const int mode)
50 throw(RuntimeException) {
52 ConfigSkeleton configSkeleton(&XPath::callbackMatch);
54 if((mode & Mode::Namespace) != 0)
55 configSkeleton.initialize = &XPath::initializeNamespaces;
57 skeleton(document, expression, configSkeleton);
59 string msg("xml::XPath::match | Expression: ");
61 msg += anna::functions::asText(" | Result: ", a_result);
62 Logger::debug(msg, ANNA_FILE_LOCATION);
67 const xml::Node* xml::XPath::find(const Document& document, const char* expression, const int mode, const Exception::Mode::_v emode)
68 throw(RuntimeException) {
69 apply(document, expression, mode);
70 Node::const_child_iterator ii = node_begin();
72 if(ii == node_end()) {
73 if(emode == Exception::Mode::Throw) {
74 string msg("xml::XPath::find | Expression: ");
76 msg += " | Does not match any xml::Node";
77 throw RuntimeException(msg, ANNA_FILE_LOCATION);
78 } else if(emode == Exception::Mode::Trace) {
79 string msg("xml::XPath::find | Expression: ");
81 msg += " | Does not match any xml::Node";
82 Logger::warning(msg, ANNA_FILE_LOCATION);
89 string msg("xml::XPath::find | Expression: ");
91 msg += functions::asString(" | Ignores %d xml::Node's", size());
92 Logger::warning(msg, ANNA_FILE_LOCATION);
95 return XPath::node(ii);
98 void xml::XPath::skeleton(const Document& document, const char* expression, xml::XPath::ConfigSkeleton& config)
99 throw(RuntimeException) {
101 string msg("xml::XPath::skeleton | Document: ");
102 msg += functions::asString(document.getContent());
103 msg += " | Expression: ";
105 Logger::debug(msg, ANNA_FILE_LOCATION);
108 if(document.a_handle == NULL)
109 throw RuntimeException("xml::XPath::pattern | xml::Document had not been initialized", ANNA_FILE_LOCATION);
111 setupEncoding(document.a_handle);
113 xmlXPathContextPtr context = NULL;
114 xmlXPathObjectPtr object = NULL;
117 if((context = xmlXPathNewContext(document.a_handle)) == NULL)
118 throw RuntimeException("Can not create XPath context", ANNA_FILE_LOCATION);
120 if(config.initialize != NULL)
121 (this->*config.initialize)(context, document);
123 if((object = xmlXPathEvalExpression(BAD_CAST expression, context)) == NULL) {
124 string msg("xml::XPath::skeleton | Expression: ");
126 msg += " | Unable to evaluate expression";
127 throw RuntimeException(msg, ANNA_FILE_LOCATION);
130 (this->*config.callback)(object->nodesetval);
131 xmlXPathFreeObject(object);
132 xmlXPathFreeContext(context);
133 } catch(RuntimeException& ex) {
135 xmlXPathFreeObject(object);
138 xmlXPathFreeContext(context);
145 * Ha que tener en cuenta que puede haber definiciones de NS en cualquier nodo
146 * lo que nos obliga a recorrerlo en profundidad.
148 void xml::XPath::initializeNamespaces(_xmlXPathContext* context, const Document& document)
149 throw(RuntimeException) {
152 if((root = xmlDocGetRootElement(document.a_handle)) == NULL)
153 throw RuntimeException("Error interpreting the XML document", ANNA_FILE_LOCATION);
155 // Incorpora la definición de los namespaces usados en el documento y los define en el contexto XPath
156 forwardNamespaces(context, root);
159 void xml::XPath::forwardNamespaces(_xmlXPathContext* context, _xmlNode* xmlNode)
160 throw(RuntimeException) {
161 while(xmlNode != NULL) {
162 if(xmlNode->type == XML_ELEMENT_NODE) {
163 for(xmlNs* ns = xmlNode->nsDef; ns != NULL; ns = ns->next) {
164 a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
165 xmlXPathRegisterNs(context, ns->prefix, ns->href);
167 string msg("xml::XPath::forwardNamespaces | Prefix: ");
168 msg += (const char*) ns->prefix;
170 msg += (const char*) ns->href;
171 Logger::debug(msg, ANNA_FILE_LOCATION);
175 forwardNamespaces(context, xmlNode->children);
178 xmlNode = xmlNode->next;
182 void xml::XPath::callbackApply(const _xmlNodeSet* nodes) {
183 /* Si no hay nodos que cumplan con la expresión */
190 for(int ii = 0, maxii = nodes->nodeNr; ii < maxii; ii ++) {
191 xmlNode = nodes->nodeTab [ii];
193 if(xmlNode->type != XML_ELEMENT_NODE)
196 /* Sólo analiza el primer nivel de dependencia de cada uno de los nodos seleccionados */
197 node = a_root->createChild((const char*) xmlNode->name);
198 attributes(node, xmlNode->properties);
200 if(xmlNode->ns != NULL)
201 node->setNamespace(a_root->namespace_find((const char*) xmlNode->ns->prefix));
203 if((a_mode & Mode::Full) != 0)
204 children(node, xmlNode->children);
206 text(node, xmlNode->children);
208 LOGDEBUG(Logger::debug(node->asString(), ANNA_FILE_LOCATION));
212 void xml::XPath::callbackMatch(const _xmlNodeSet* nodes) {
213 a_result = (nodes == NULL) ? false : (nodes->nodeNr > 0);
216 void xml::XPath::text(Node* node, xmlNode* xmlNode)
217 throw(RuntimeException) {
221 while(xmlNode != NULL) {
222 switch(xmlNode->type) {
224 w = (const char*) xmlNode->content;
228 if(isspace(*w) == false) {
236 if(isSeparator == false)
237 node->createText(decode(xmlNode->content));
245 xmlNode = xmlNode->next;