Annotation of gpl/axl/ns/axl_ns_doc.c, revision 1.1.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>