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 const xml::Node* result = NULL;
71 Node::const_child_iterator ii = node_begin();
73 if(ii == node_end()) {
74 if(emode == Exception::Mode::Throw) {
75 string msg("xml::XPath::find | Expression: ");
77 msg += " | Does not match any xml::Node";
78 throw RuntimeException(msg, ANNA_FILE_LOCATION);
79 } else if(emode == Exception::Mode::Trace) {
80 string msg("xml::XPath::find | Expression: ");
82 msg += " | Does not match any xml::Node";
83 Logger::warning(msg, ANNA_FILE_LOCATION);
90 string msg("xml::XPath::find | Expression: ");
92 msg += functions::asString(" | Ignores %d xml::Node's", size());
93 Logger::warning(msg, ANNA_FILE_LOCATION);
96 return XPath::node(ii);
99 void xml::XPath::skeleton(const Document& document, const char* expression, xml::XPath::ConfigSkeleton& config)
100 throw(RuntimeException) {
102 string msg("xml::XPath::skeleton | Document: ");
103 msg += functions::asString(document.getContent());
104 msg += " | Expression: ";
106 Logger::debug(msg, ANNA_FILE_LOCATION);
109 if(document.a_handle == NULL)
110 throw RuntimeException("xml::XPath::pattern | xml::Document had not been initialized", ANNA_FILE_LOCATION);
112 setupEncoding(document.a_handle);
114 xmlXPathContextPtr context = NULL;
115 xmlXPathObjectPtr object = NULL;
118 if((context = xmlXPathNewContext(document.a_handle)) == NULL)
119 throw RuntimeException("Can not create XPath context", ANNA_FILE_LOCATION);
121 if(config.initialize != NULL)
122 (this->*config.initialize)(context, document);
124 if((object = xmlXPathEvalExpression(BAD_CAST expression, context)) == NULL) {
125 string msg("xml::XPath::skeleton | Expression: ");
127 msg += " | Unable to evaluate expression";
128 throw RuntimeException(msg, ANNA_FILE_LOCATION);
131 (this->*config.callback)(object->nodesetval);
132 xmlXPathFreeObject(object);
133 xmlXPathFreeContext(context);
134 } catch(RuntimeException& ex) {
136 xmlXPathFreeObject(object);
139 xmlXPathFreeContext(context);
146 * Ha que tener en cuenta que puede haber definiciones de NS en cualquier nodo
147 * lo que nos obliga a recorrerlo en profundidad.
149 void xml::XPath::initializeNamespaces(_xmlXPathContext* context, const Document& document)
150 throw(RuntimeException) {
153 if((root = xmlDocGetRootElement(document.a_handle)) == NULL)
154 throw RuntimeException("Error interpreting the XML document", ANNA_FILE_LOCATION);
156 // Incorpora la definición de los namespaces usados en el documento y los define en el contexto XPath
157 forwardNamespaces(context, root);
160 void xml::XPath::forwardNamespaces(_xmlXPathContext* context, _xmlNode* xmlNode)
161 throw(RuntimeException) {
162 while(xmlNode != NULL) {
163 if(xmlNode->type == XML_ELEMENT_NODE) {
164 for(xmlNs* ns = xmlNode->nsDef; ns != NULL; ns = ns->next) {
165 a_root->createNamespace((const char*) ns->prefix, (const char*) ns->href);
166 xmlXPathRegisterNs(context, ns->prefix, ns->href);
168 string msg("xml::XPath::forwardNamespaces | Prefix: ");
169 msg += (const char*) ns->prefix;
171 msg += (const char*) ns->href;
172 Logger::debug(msg, ANNA_FILE_LOCATION);
176 forwardNamespaces(context, xmlNode->children);
179 xmlNode = xmlNode->next;
183 void xml::XPath::callbackApply(const _xmlNodeSet* nodes) {
184 /* Si no hay nodos que cumplan con la expresión */
191 for(int ii = 0, maxii = nodes->nodeNr; ii < maxii; ii ++) {
192 xmlNode = nodes->nodeTab [ii];
194 if(xmlNode->type != XML_ELEMENT_NODE)
197 /* Sólo analiza el primer nivel de dependencia de cada uno de los nodos seleccionados */
198 node = a_root->createChild((const char*) xmlNode->name);
199 attributes(node, xmlNode->properties);
201 if(xmlNode->ns != NULL)
202 node->setNamespace(a_root->namespace_find((const char*) xmlNode->ns->prefix));
204 if((a_mode & Mode::Full) != 0)
205 children(node, xmlNode->children);
207 text(node, xmlNode->children);
209 LOGDEBUG(Logger::debug(node->asString(), ANNA_FILE_LOCATION));
213 void xml::XPath::callbackMatch(const _xmlNodeSet* nodes) {
214 a_result = (nodes == NULL) ? false : (nodes->nodeNr > 0);
217 void xml::XPath::text(Node* node, xmlNode* xmlNode)
218 throw(RuntimeException) {
222 while(xmlNode != NULL) {
223 switch(xmlNode->type) {
225 w = (const char*) xmlNode->content;
229 if(isspace(*w) == false) {
237 if(isSeparator == false)
238 node->createText(decode(xmlNode->content));
243 xmlNode = xmlNode->next;