File:  [ELWIX - Embedded LightWeight unIX -] / gpl / axl / ns / axl_ns_doc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 8 07:09:12 2011 UTC (13 years ago) by misho
Branches: axl, MAIN
CVS tags: HEAD, AXL0_6_7, AXL0_6_1
3th party - XML

    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>&lt;shaper:xml-rpc-invoke&gt;</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>