Annotation of gpl/axl/ns/axl_ns_doc.c, revision 1.1
1.1 ! misho 1: /*
! 2: * LibAxl: Another XML library
! 3: * Copyright (C) 2006 Advanced Software Production Line, S.L.
! 4: *
! 5: * This program is free software; you can redistribute it and/or
! 6: * modify it under the terms of the GNU Lesser General Public License
! 7: * as published by the Free Software Foundation; either version 2.1 of
! 8: * the License, or (at your option) any later version.
! 9: *
! 10: * This program is distributed in the hope that it will be useful,
! 11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 13: * GNU Lesser General Public License for more details.
! 14: *
! 15: * You should have received a copy of the GNU Lesser General Public
! 16: * License along with this program; if not, write to the Free
! 17: * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
! 18: * 02111-1307 USA
! 19: *
! 20: * You may find a copy of the license under this software is released
! 21: * at COPYING file. This is LGPL software: you are welcome to
! 22: * develop proprietary applications using this library without any
! 23: * royalty or fee but returning back any change, improvement or
! 24: * addition in the form of source code, project image, documentation
! 25: * patches, etc.
! 26: *
! 27: * For commercial support on build XML enabled solutions contact us:
! 28: *
! 29: * Postal address:
! 30: * Advanced Software Production Line, S.L.
! 31: * Edificio Alius A, Oficina 102,
! 32: * C/ Antonio Suarez Nº 10,
! 33: * Alcalá de Henares 28802 Madrid
! 34: * Spain
! 35: *
! 36: * Email address:
! 37: * info@aspl.es - http://www.aspl.es/xml
! 38: */
! 39: #include <axl_ns.h>
! 40:
! 41: #define LOG_DOMAIN "axl-ns-doc"
! 42:
! 43: /**
! 44: * \defgroup axl_ns_doc_module Axl Doc Namespace: Xml 1.0 namespace support for XML documents
! 45: */
! 46:
! 47: /**
! 48: * \addtogroup axl_ns_doc_module
! 49: * @{
! 50: */
! 51:
! 52: /**
! 53: * @internal Name of the key used to access and store the ns table for
! 54: * each node.
! 55: */
! 56: #define NS_TABLE "__axl:ns-table"
! 57:
! 58:
! 59: /**
! 60: * @internal Table used to hold bindings from prefix declared and
! 61: * namespaces used. It also holds the default namespace declared for a
! 62: * particular node, refering to it and all its content until found
! 63: * another declaration.
! 64: */
! 65: typedef struct _axlNsTable {
! 66: /* bindings between prefixes and namespaces */
! 67: axlHash * table;
! 68:
! 69: /* default namespace to be used */
! 70: const char * defaultNs;
! 71: } axlNsTable;
! 72:
! 73: void __axl_ns_free_table (axlNsTable * table)
! 74: {
! 75: if (table == NULL)
! 76: return;
! 77:
! 78: /* free table */
! 79: axl_hash_free (table->table);
! 80:
! 81: /* free node */
! 82: axl_free (table);
! 83:
! 84: return;
! 85: } /* end __axl_ns_free_table */
! 86:
! 87: /* check and install all ns declerations found on this node */
! 88: axl_bool __axl_ns_node_check_and_install_ns_decls (axlNode * node, axlAttrCursor * cursor, axlError ** error)
! 89: {
! 90: const char * attr;
! 91: axl_bool default_found = axl_false;
! 92: axlNsTable * ns_table = NULL;
! 93:
! 94: /* for each attribute installed on the node */
! 95: while (axl_node_attr_cursor_has_item (cursor)) {
! 96:
! 97: /* get the key */
! 98: attr = axl_node_attr_cursor_get_key (cursor);
! 99: if (! axl_memcmp (attr, "xmlns", 5)) {
! 100: /* no xml namespace declaration found, go next */
! 101: axl_node_attr_cursor_next (cursor);
! 102: continue;
! 103: } /* end if */
! 104:
! 105: /* create the table (without creating the hash for now) */
! 106: if (ns_table == NULL) {
! 107: ns_table = axl_new (axlNsTable, 1);
! 108:
! 109: /* install the table in the node */
! 110: axl_node_annotate_data_full (node,
! 111: /* key and its destroy function */
! 112: NS_TABLE, NULL,
! 113: /* value and its destroy function */
! 114: ns_table, (axlDestroyFunc)__axl_ns_free_table);
! 115: } /* end if */
! 116:
! 117: /* check if we have a default xmlns declaration */
! 118: if (strlen (attr) == 5) {
! 119: /* check if there were previous xmlns default declarations */
! 120: if (default_found) {
! 121: /* free cursor */
! 122: axl_node_attr_cursor_free (cursor);
! 123:
! 124: axl_error_new (-1, "Found that the document defines several default xmlns declarations at the same node", NULL, error);
! 125: return axl_false;
! 126: } /* end if */
! 127: default_found = axl_true;
! 128:
! 129: /* copy the default namespace */
! 130: ns_table->defaultNs = axl_node_attr_cursor_get_value (cursor);
! 131:
! 132: /* go next */
! 133: axl_node_attr_cursor_next (cursor);
! 134: continue;
! 135:
! 136: } /* end if */
! 137:
! 138: /* found preffixed namespace declaration */
! 139: if (ns_table->table == NULL) {
! 140: /* create the table with 1 step */
! 141: ns_table->table = axl_hash_new_full (axl_hash_string, axl_hash_equal_string, 1);
! 142: } /* end if */
! 143:
! 144: /* check that the prefix wasn't declared at the same node */
! 145: attr = attr + 6;
! 146: if (axl_hash_exists (ns_table->table, (axlPointer) attr)) {
! 147: /* free cursor */
! 148: axl_node_attr_cursor_free (cursor);
! 149:
! 150: axl_error_new (-1, "Found that the document already declares the same prefix for the same node several times",
! 151: NULL, error);
! 152: return axl_false;
! 153: } /* end if */
! 154:
! 155: /* install it in the current ns hash table */
! 156: axl_hash_insert (ns_table->table, (axlPointer) attr, (axlPointer) axl_node_attr_cursor_get_value (cursor));
! 157:
! 158: /* get next attribute */
! 159: axl_node_attr_cursor_next (cursor);
! 160:
! 161: } /* end while */
! 162:
! 163: /* all declarations done */
! 164: return axl_true;
! 165:
! 166: } /* end __axl_ns_node_check_and_install_ns_decls */
! 167:
! 168: /**
! 169: * @internal Function that validates the node against current status
! 170: * of the namespace support.
! 171: */
! 172: axl_bool __axl_ns_node_validate (axlNode * node, axlError ** error)
! 173: {
! 174: int iterator = 0;
! 175: char * name;
! 176: axl_bool found = axl_false;
! 177: axlNsTable * ns_table;
! 178: axlNode * parent;
! 179: axlNode * child;
! 180:
! 181: /* create the cursor */
! 182: axlAttrCursor * cursor = NULL;
! 183:
! 184: /* check if the node has a namespace declaration */
! 185: if (axl_node_has_attributes (node)) {
! 186:
! 187: /* the node has attributes, check if there are some
! 188: * namespace declaration */
! 189: cursor = axl_node_attr_cursor_new (node);
! 190:
! 191: /* check and install all ns declerations found on this
! 192: * node (cursor is dellocated by this function on
! 193: * error) */
! 194: if (! __axl_ns_node_check_and_install_ns_decls (node, cursor, error))
! 195: return axl_false;
! 196:
! 197: } /* end if */
! 198:
! 199: /* now we have all namespace declarations, check that the node
! 200: * name and its attributes are properly declared */
! 201: name = (char*) axl_node_get_name (node);
! 202:
! 203: /* check if the node name uses a namespace scope declaration
! 204: * (prefix:name) */
! 205: while (name[iterator] != 0) {
! 206: if (name[iterator] == ':') {
! 207: /* set the terminator */
! 208: name[iterator] = 0;
! 209:
! 210: /* namespace node found */
! 211: found = axl_true;
! 212: break;
! 213: }
! 214:
! 215: /* get next position */
! 216: iterator++;
! 217: } /* end if */
! 218:
! 219: if (found) {
! 220: /* namespace declaration found (this supposes that the
! 221: * default ns is not used) */
! 222: parent = node;
! 223: while (parent != NULL) {
! 224: /* try to get the ns table having the prefix declaration */
! 225: ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
! 226:
! 227: /* check the namespace */
! 228: if (ns_table != NULL && axl_hash_exists (ns_table->table, name)) {
! 229: /* prefix found, restore node name */
! 230: name[iterator] = ':';
! 231: break;
! 232: } /* end if */
! 233:
! 234: /* get the parent */
! 235: parent = axl_node_get_parent (parent);
! 236:
! 237: /* no more parents */
! 238: if (parent == NULL) {
! 239: /* prefix not found, restore node name */
! 240: name[iterator] = ':';
! 241:
! 242: /* free cursor */
! 243: axl_node_attr_cursor_free (cursor);
! 244:
! 245: axl_error_new (-1, "Found prefix declaration for which a namespace binding wasn't found in the node or any parent (xmlns:prefix='ns')",
! 246: NULL, error);
! 247: return axl_false;
! 248: } /* end if */
! 249: } /* end while */
! 250: } /* end if */
! 251:
! 252: /* check if the node has a namespace declaration */
! 253: if (axl_node_has_attributes (node)) {
! 254: /* now check attributes */
! 255: axl_node_attr_cursor_first (cursor);
! 256: while (axl_node_attr_cursor_has_item (cursor)) {
! 257:
! 258: /* get the key */
! 259: name = (char*) axl_node_attr_cursor_get_key (cursor);
! 260: if (axl_memcmp (name, "xmlns", 5)) {
! 261: /* skip namespace declarations */
! 262: axl_node_attr_cursor_next (cursor);
! 263: continue;
! 264: } /* end if */
! 265:
! 266: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking attribute name='%s'", name);
! 267:
! 268: /* check if the node name uses a namespace scope declaration
! 269: * (prefix:name) */
! 270: found = axl_false;
! 271: iterator = 0;
! 272: while (name[iterator] != 0) {
! 273: if (name[iterator] == ':') {
! 274: /* set the terminator */
! 275: name[iterator] = 0;
! 276:
! 277: /* namespace node found */
! 278: found = axl_true;
! 279: break;
! 280: }
! 281:
! 282: /* get next position */
! 283: iterator++;
! 284: } /* end while */
! 285:
! 286: if (found) {
! 287: parent = node;
! 288: while (parent != NULL) {
! 289: /* try to get the ns table having the prefix declaration */
! 290: ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
! 291:
! 292: /* check the namespace */
! 293: if (ns_table != NULL && axl_hash_exists (ns_table->table, name)) {
! 294: /* prefix found, restore attribute name */
! 295: name[iterator] = ':';
! 296: break;
! 297: } /* end if */
! 298:
! 299: /* get the parent */
! 300: parent = axl_node_get_parent (parent);
! 301:
! 302: /* no more parents */
! 303: if (parent == NULL) {
! 304: /* prefix not found, restore attribute name */
! 305: name[iterator] = ':';
! 306:
! 307: /* free cursor */
! 308: axl_node_attr_cursor_free (cursor);
! 309:
! 310: axl_error_new (-1, "Found prefix declaration, for an attribute, for which a namespace binding wasn't found in the node or any parent (xmlns:prefix='ns')",
! 311: NULL, error);
! 312: return axl_false;
! 313: } /* end if */
! 314: } /* end while */
! 315: } /* end if */
! 316:
! 317: /* next attribute */
! 318: axl_node_attr_cursor_next (cursor);
! 319:
! 320: } /* end while */
! 321:
! 322: /* free cursor */
! 323: axl_node_attr_cursor_free (cursor);
! 324:
! 325: } /* end if */
! 326:
! 327: /* do the same for the rest of the child nodes */
! 328: child = axl_node_get_first_child (node);
! 329: while (child != NULL) {
! 330: /* check childs */
! 331: if (! __axl_ns_node_validate (child, error))
! 332: return axl_false;
! 333:
! 334: /* go next child node */
! 335: child = axl_node_get_next (child);
! 336: } /* end while */
! 337:
! 338: /* return axl_true, if reached this place */
! 339: return axl_true;
! 340: }
! 341:
! 342: /**
! 343: * @brief Allows to check XML 1.0 namespace rules for the document
! 344: * loaded.
! 345: *
! 346: * XML 1.0 Namespaces definition allows you to define documents that
! 347: * are resistent to tag clashing, making your product to be more
! 348: * usable at any level, being mixed with other products.
! 349: *
! 350: * This function allows you to check a xml document, already loaded
! 351: * with the Axl Doc base API (\ref axl_doc_parse_from_file, \ref
! 352: * axl_doc_parse or \ref axl_doc_create), if it follows the namespaces
! 353: * contrains (XML 1.0 Namespaces http://www.w3.org/TR/REC-xml-names/).
! 354: *
! 355: * The idea behind this function is to ensure that the document has
! 356: * the proper XML 1.0 Namespace declarations, which will be used by
! 357: * the following function to help you detect those tags recognized by
! 358: * your XML software, ensuring this matching is done inside your
! 359: * namespace:
! 360: *
! 361: * - \ref axl_ns_node_cmp
! 362: * - \ref axl_ns_node_find_called
! 363: * - See \ref axl_ns_node_module "Axl namespace support for nodes" to get more information about function used to read your xml documents in a namespace aware manner.
! 364: *
! 365: * Here is a simple example on how to load a xml document, add
! 366: * namespace checking support, and read content inside without
! 367: * compromissing your code to node tag collitions:
! 368: *
! 369: * \include ns_example.c
! 370: *
! 371: * As a gold rule, your code must not use prefixed names (full
! 372: * qualified names) to check xml node names (tags), because they will
! 373: * make your code fragile to changes introduced in xml documents read
! 374: * by your application.
! 375: *
! 376: * Instead, you must provide your namespace where the validation will take
! 377: * place and the local name of the node being checked (knowing that
! 378: * the prefix and the local name for <b><shaper:xml-rpc-invoke></b> is
! 379: * <b>shaper</b> and <b>xml-rpc-invoke</b> respectively).
! 380: *
! 381: * Let's see an an example to clarify this. Assuming the following xml document:
! 382: *
! 383: * <div class="xml-doc">
! 384: * \include ns_shaper.xml
! 385: * </div>
! 386: *
! 387: * Previous examples shows a simple shaper description, which is using
! 388: * <b>shaper</b> as prefix for its nodes. Under this situation you
! 389: * must not use the following to check for an xml node name:
! 390: *
! 391: * \code
! 392: * if (NODE_CMP_NAME (node, "shaper:xml-rpc-invoke")) {
! 393: * // found shaper:xml-rpc-invoke tag
! 394: * }
! 395: * \endcode
! 396: *
! 397: * This is because, as we have said, you are placing direct references
! 398: * to the namespace prefix declared at the document, but this is
! 399: * wrong. You can't ensure the user won't change the namespace binding
! 400: * that links your namespace with the prefix <b>shaper</b>.
! 401: *
! 402: * The proper way to check xml node names (tags) in a namespace aware
! 403: * manner is:
! 404: *
! 405: * \code
! 406: * // supposing SHAPER_NS defines the namespace string
! 407: * if (axl_ns_node_cmp (node, SHAPER_NS, "xml-rpc-invoke")) {
! 408: * // found xml-rpc-invoke tag
! 409: * }
! 410: * \endcode
! 411: *
! 412: * Now, the user will be able to change the binding between your
! 413: * namespace and the prefix used. This will enable him to use the
! 414: * prefix <b>shaper</b> for its products, without breaking your
! 415: * software.
! 416: *
! 417: * <i><b>NOTE:</b> providing a document without content (at least one
! 418: * root node configured), will cause the function to return \ref
! 419: * axl_true.</i>
! 420: *
! 421: * @param doc The document that is being required to be checked
! 422: * against the XML Namespaces 1.0 rules.
! 423: *
! 424: * @param error An optional variable where errors will be reported. If
! 425: * the function returns \ref axl_false, you can call to \ref axl_error_get
! 426: * to get a textual diagnostic.
! 427: *
! 428: * @return \ref axl_true if the document is namespace-valid, otherwise,
! 429: * \ref axl_false is returned.
! 430: */
! 431: axl_bool axl_ns_doc_validate (axlDoc * doc, axlError ** error)
! 432: {
! 433: axlNode * node;
! 434:
! 435: /* check references */
! 436: if (doc == NULL) {
! 437: axl_error_new (-1, "Document provided is a null reference", NULL, error);
! 438: return axl_false;
! 439: } /* end if */
! 440:
! 441: /* get root document */
! 442: node = axl_doc_get_root (doc);
! 443: if (node == NULL) {
! 444: /* document has no nodes, return axl_true */
! 445: return axl_true;
! 446: } /* end if */
! 447:
! 448: /* call to produce and validate namespace content */
! 449: return __axl_ns_node_validate (node, error);
! 450: }
! 451:
! 452: /**
! 453: * @internal Internal API that allows to check if the provided prefix
! 454: * (ns_prefix) is bound to the provided namespace, using the node
! 455: * provided as a reference for the parent lookup.
! 456: *
! 457: * @param node The node that is used as reference for the
! 458: * lookup. Starting from this node the lookup will be performed,
! 459: * following with parents.
! 460: *
! 461: * @param ns_prefix The ns prefix to be checked.
! 462: *
! 463: * @param ns The namespace that must be bound the prefix.
! 464: *
! 465: * @return axl_true if the prefix is bound to the namespace provided,
! 466: * using the node as reference for all declarations found inside the
! 467: * particular node (or its parents).
! 468: */
! 469: axl_bool axl_ns_doc_node_check (axlNode * node,
! 470: const char * ns_prefix,
! 471: const char * ns)
! 472: {
! 473: axlNode * parent = node;
! 474: axlNsTable * ns_table = NULL;
! 475:
! 476: /* foreach node, up to the root parent, starting from the node
! 477: * provided, do: */
! 478: while (parent != NULL) {
! 479: /* try to get the ns table having the prefix declaration */
! 480: ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
! 481:
! 482: /* check the namespace */
! 483: if (ns_table != NULL && axl_hash_exists (ns_table->table, (axlPointer) ns_prefix)) {
! 484: /* prefix found, check its value */
! 485: return axl_cmp (axl_hash_get (ns_table->table, (axlPointer) ns_prefix), ns);
! 486: } /* end if */
! 487:
! 488: /* not found, get the parent */
! 489: parent = axl_node_get_parent (parent);
! 490:
! 491: } /* end while */
! 492:
! 493: /* reached this point, return axl_false */
! 494: return axl_false;
! 495: }
! 496:
! 497: /**
! 498: * @internal Function that allows to check if the default namespace is
! 499: * the value provided, using as reference, the node received.
! 500: *
! 501: * @param node The node to check for its default namespace to match
! 502: * the value provided.
! 503: *
! 504: * @param ns The namespace that is provided to match.
! 505: *
! 506: * @return \ref axl_true if the node has as default namespace the value
! 507: * received. Otherwise \ref axl_false is returned.
! 508: */
! 509: axl_bool axl_ns_doc_check_default (axlNode * node,
! 510: const char * ns)
! 511: {
! 512: axlNode * parent = node;
! 513: axlNsTable * ns_table = NULL;
! 514:
! 515: /* foreach node, up to the root parent, starting from the node
! 516: * provided, do: */
! 517: while (parent != NULL) {
! 518: /* try to get the ns table having the prefix declaration */
! 519: ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
! 520:
! 521: /* check the namespace */
! 522: if (ns_table != NULL && ns_table->defaultNs != NULL) {
! 523: /* prefix found, check its value */
! 524: return axl_cmp (ns_table->defaultNs, ns);
! 525: } /* end if */
! 526:
! 527: /* not found, get the parent */
! 528: parent = axl_node_get_parent (parent);
! 529:
! 530: } /* end while */
! 531:
! 532: /* reached this point, return axl_false */
! 533: return axl_false;
! 534: }
! 535:
! 536: /**
! 537: * @}
! 538: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>