Annotation of gpl/axl/src/axl_dtd.c, revision 1.1.1.2
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_decl.h>
40: #include <axl.h>
41:
42: #define LOG_DOMAIN "axl-dtd"
43:
44: struct _axlDtdElementListNode {
45: NodeType type;
46: AxlDtdTimes times;
47: axlPointer data;
48: };
49:
50: struct _axlDtdElementList {
51: /**
52: * @brief Allows to configure how is given top level
53: * configuration for nodes to be defined inside the xml
54: * document being configured. As defined in the XML 1.0
55: * Recomendation, available top level choices are: choice or
56: * sequence.
57: *
58: * They allow to configure allowed nodes to be selected as
59: * childs, from a set of node names, called choice or to
60: * configure which are the set of nodes to be used, in a
61: * particular order, called sequence.
62: *
63: * This variable allows to configure which is the top level
64: * section configuration: either a choice or a sequence.
65: *
66: * Keep in mind that, having only one element inside the
67: * itemList, there is no difference between the sequence and
68: * the choice.
69: */
70: AxlDtdNestedType type;
71:
72: /**
73: * @brief Allows to configure how many times is repeated a
74: * selection provided (by this element).
75: */
76: AxlDtdTimes times;
77:
78: /**
79: * @brief Item list, which contains more axlDtdElementList
80: * nodes, configuring elements allowed.
81: */
82: axlList * itemList;
83: };
84:
85: struct _axlDtdElement {
86: /**
87: * @brief The document type element declaration name. This is
88: * the name of the xml node being constrained.
89: */
90: char * name;
91:
92: /**
93: * @brief This is the type of the xml node being constrained.
94: */
95: AxlDtdElementType type;
96: /**
97: * @brief List of available items.
98: *
99: * This variable holds current top level list selection. See
100: * axlDtdElementList.type variable.
101: */
102: axlDtdElementList * list;
103:
104: /**
105: * @brief Minimum item list count to be matched while using
106: * this DTD element rule.
107: */
108: int minimum_match;
109: };
110:
111: struct _axlDtdAttributeDecl {
112: /**
113: * @brief Attribute name. This is the attribute value defined
114: * for the node.
115: */
116: char * name;
117:
118: /**
119: * @brief This is the attribute declaration type. It shows if
120: */
121: AxlDtdAttributeType type;
122:
123: /**
124: * @brief Allows to model whoe is
125: */
126: AxlDtdAttributeDefaults defaults;
127:
128: /**
129: * @brief This is a default value for the <!ATTLIST
130: * declaration received, in the case a FIXED value is required
131: * or a default value is declarted.
132: */
133: char * default_value;
134:
135: /**
136: * @brief Internal declaration for enum values defined for
137: * this rule. This list is only initialized in the case enum
138: * values are defined.
139: */
140: axlList * enumvalues;
141:
142: };
143:
144: struct _axlDtdAttribute {
145: /**
146: * @brief The document attribute list declaration name. This
147: * is the name of the node that will receive the constrain
148: * defined.
149: */
150: char * name;
151:
152: /**
153: * @brief This is the list of constrains defined for the
154: * node. It as list of \ref axlDtdAttributeDecl which defines
155: * the attribute that is declarted, if it is required, and the
156: * type of its content.
157: */
158: axlList * list;
159: };
160:
161: struct _axlDtdEntityExternalData {
162: /**
163: * @brief Contains the system literal reference. This is a URI
164: * reference to the resource pointed by the \ref axlDtdEntity
165: * definition.
166: */
167: char * system_literal;
168: /**
169: * @brief Contains the public literal information associated
170: * to the entity definition.
171: */
172: char * public_literal;
173: /**
174: * @brief Contains the NDATA information (a notation name
175: * reference).
176: */
177: char * ndata;
178: };
179:
180: struct _axlDtdEntity {
181: /**
182: * @brief Contains the entity name.
183: */
184: char * name;
185:
186: /**
187: * @brief Contains the entity type.
188: */
189: axlDtdEntityType type;
190:
191: /**
192: * @brief Content of the entity definition ([9] EntityValue).
193: */
194: char * content;
195:
196: /**
197: * @brief An entity definition can have a reference to a
198: * external resource. The following pointer contains
199: * information for the external resource pointed.
200: */
201: axlDtdEntityExternalData * data;
202: };
203:
204: struct _axlDtd {
205: /**
206: * @brief Holds all entity definitions inside the DTD
207: * declaration (<!ENTITY..>).
208: */
209: axlList * entities;
210:
211: /**
212: * @brief All elements inside the DTD declaration
213: * (<!ELEMENT..> ).
214: */
215: axlList * elements;
216:
217: /**
218: * @brief All attribute type declerations inside the DTD
219: * (<!ATTLIST..>)
220: */
221: axlList * attributes;
222:
223: /**
224: * @brief The element root, for the given DTD declaration.
225: */
226: axlDtdElement * root;
227:
228: /**
229: * @brief Internal flag that allows to notify that the DTD
230: * contains ID attribute declaration, making the DTD this
231: * references.
232: */
233: axl_bool haveIdDecl;
234:
235: /**
236: * @brief Flag that the dtd declaration have attributes which
237: * are flaged as IDREF.
238: */
239: axl_bool haveIdRefDecl;
240: };
241:
242: /**
243: * \defgroup axl_dtd_module Axl DTD: Document type declaration interface (functions, validation, and DTD parsing)
244: */
245:
246:
247: /**
248: * \addtogroup axl_dtd_module
249: * @{
250: */
251:
252:
253: /**
254: * @internal
255: *
256: * Allows to create a new dtd element list item, which represents a
257: * content particule inside an item list or a item list. This allows
258: * the recursion defined on the XML 1.0 standard.
259: *
260: * The function receives the node name and a reference list. According
261: * to the values the function creates a node which contains a leaf
262: * value or a node which contains a reference to the a new list which
263: * is nested.
264: */
265: axlDtdElementListNode * __create_axl_dtd_element_list (char * node_name,
266: axlDtdElementList * list)
267: {
268: axlDtdElementListNode * node;
269:
270: node = axl_new (axlDtdElementListNode, 1);
271:
272: /* create a node element reference */
273: if (node_name != NULL) {
274: node->data = node_name;
275: node->type = AXL_ELEMENT_NODE;
276: return node;
277: }
278:
279: /* create an element list reference */
280: if (list != NULL) {
281: node->data = list;
282: node->type = AXL_ELEMENT_LIST;
283: return node;
284: }
285:
286: /* if another type is requested, return NULL */
287: return NULL;
288: }
289:
290: /**
291: * @internal
292: *
293: * Support function used to destroy all items stored on a item list.
294: *
295: * @param node
296: */
297: void __destroy_axl_dtd_element_list (axlDtdElementListNode * node)
298: {
299: if (node == NULL)
300: return;
301: /* free the reference to the leaf node if defined */
302: if (node->type == AXL_ELEMENT_NODE)
303: axl_free (node->data);
304:
305: /* do not do nothing if the reference is not element list */
306: if (node->type == AXL_ELEMENT_LIST)
307: axl_dtd_item_list_free (node->data);
308:
309: /* free de node itself */
310: axl_free (node);
311: return;
312: }
313:
314: /**
315: * @internal
316: *
317: * @brief Support function to \ref axl_dtd_parse which creates a new
318: * empty DTD reference.
319: *
320: *
321: * @return A newly allocated \ref axlDtd reference.
322: */
323: axlDtd * __axl_dtd_new (void)
324: {
325: axlDtd * dtd;
326:
327: /* create the DTD element and nothing else. The rest of items
328: * created on demand */
329: dtd = axl_new (axlDtd, 1);
330:
331: return dtd;
332: }
333:
334: axl_bool __queue_items (axlPointer data, axlPointer _stack)
335: {
336: axlStack * stack = _stack;
337:
338: /* queue the data */
339: axl_stack_push (stack, data);
340:
341: /* return axl_false to make the function to not stop */
342: return axl_false;
343: }
344:
345: void __axl_dtd_queue_childs (axlStack * stack, axlNode * parent)
346: {
347: axlNode * child;
348:
349: /* get the first child */
350: child = axl_node_get_first_child (parent);
351: while (child != NULL) {
352:
353: /* queue the child */
354: axl_stack_push (stack, child);
355:
356: /* get the next child */
357: child = axl_node_get_next (child);
358: } /* end while */
359:
360: return;
361: }
362:
363: /**
364: * @internal
365: *
366: * Support internal function which allows to queue all items inside an
367: * axlDtdElementList to be checked.
368: *
369: * @param stack The stack where all data will be placed.
370: *
371: * @param dtd_element_list The dtd element list where the data will be
372: * extracted.
373: */
374: void __axl_dtd_queue_items (axlStack * stack, axlList * list)
375: {
376: /* call to queue items */
377: axl_list_lookup (list, __queue_items, stack);
378:
379: /* nothing more */
380: return;
381: }
382:
383: /**
384: * @internal
385: *
386: * Support function which allows to check if the provided two dtd
387: * elements are in fact, parent and child.
388: *
389: * DTD element have a parent-child relation based in the fact that the
390: * first define top level xml nodes that are followed, in the form of
391: * childs nodes, by other DTD elements that define more childs, etc...
392: *
393: * This function allows to check if the provided parent dtd element
394: * have references inside its content specification that proves that
395: * it is indeed a parent definition.
396: *
397: * @param dtd_element_parent The supposed DTD parent element.
398: * @param dtd_element_child The supposedd DTD child element.
399: *
400: * @return \ref axl_true if the function can confirm that the parent-child
401: * relation exists, \ref axl_false if not or it could be proved.
402: */
403: axl_bool __axl_dtd_get_is_parent (axlDtdElement * dtd_element_parent,
404: axlDtdElement * dtd_element_child)
405: {
406: axlStack * stack;
407: axlDtdElementListNode * node;
408: axlDtdElementList * list;
409:
410: /* check for leaf nodes, that, by definition, could be a
411: * parent of nothing. */
412: if (dtd_element_parent->list == NULL || dtd_element_parent->list->itemList == NULL) {
413: return axl_false;
414: }
415:
416: /* prepare all elements inside the stack to be checked */
417: stack = axl_stack_new (NULL);
418: __axl_dtd_queue_items (stack, dtd_element_parent->list->itemList);
419:
420:
421: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "stack size to operate: %d, list: %d",
422: axl_stack_size (stack),
423: axl_list_length (dtd_element_parent->list->itemList));
424:
425: /* now search for a content particule that makes are reference
426: * to the child DTD element */
427: do {
428: node = axl_stack_pop (stack);
429: switch (node->type) {
430: case AXL_ELEMENT_NODE:
431: /* leaf node case */
432: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a leaf node, checking it");
433:
434: /* seems this is a final node */
435: if (axl_cmp (node->data, dtd_element_child->name)) {
436: /* seems that the content
437: * specification makes a reference to
438: * the child node. */
439: axl_stack_free (stack);
440: return axl_true;
441: }
442: break;
443: case AXL_ELEMENT_LIST:
444: /* a nested list case */
445: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a complex node queuing its internal elements, while checking parent=%s for child=%s",
446: dtd_element_parent->name, dtd_element_child->name);
447: /* the item list read, is a complex value,
448: * queue all items inside to be inspected */
449: list = node->data;
450: __axl_dtd_queue_items (stack, list->itemList);
451: break;
452: case AXL_ELEMENT_NOT_DEFINED:
453: /* do nothing */
454: break;
455: }
456:
457: /* iterate until all elements are evaluated */
458: }while (! axl_stack_is_empty (stack));
459:
460: /* deallocates no longer used stack */
461: axl_stack_free (stack);
462:
463: /* either it isn't the parent or it can't be proved. */
464: return axl_false;
465: }
466:
467:
468: /**
469: * @internal
470: *
471: * Support function which allows to get which is the most top root
472: * node for the provided set of DTD elements.
473: */
474: axlDtdElement * __axl_dtd_get_new_root (axlDtd * dtd)
475: {
476: int iterator;
477: axl_bool change_detected;
478:
479: axlDtdElement * dtd_element_aux;
480: axlDtdElement * dtd_element_the_root_is_on_fire;
481:
482: /* set the very first root node */
483: dtd_element_the_root_is_on_fire = axl_list_get_nth (dtd->elements, 0);
484:
485: do {
486: /* check which is the top */
487: iterator = 0;
488: change_detected = axl_false;
489: while (iterator < axl_list_length (dtd->elements)) {
490:
491: /* get the next reference */
492: dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
493:
494: /* check which is the top */
495: if (__axl_dtd_get_is_parent (dtd_element_aux,
496: dtd_element_the_root_is_on_fire)) {
497: /* it seems that the new element is the root
498: * one, update the reference */
499: dtd_element_the_root_is_on_fire = dtd_element_aux;
500: change_detected = axl_true;
501: }
502:
503: /* update inner loop iterator */
504: iterator ++;
505: } /* while end */
506: }while (change_detected);
507:
508: /* return the root found */
509: return dtd_element_the_root_is_on_fire;
510: }
511:
512: /**
513: * @internal
514: *
515: * @brief Adds the axlDtdElement into the given axlDtd definition,
516: * checking that everything is properly configured, and ensuring that
517: * the root element gets properly configured.
518: *
519: * @param dtd The \ref axlDtd object that will receive the
520: * axlDtdElement.
521: *
522: * @param stream The \ref axlStream object that will be destroyed if
523: * something wrong is found.
524: *
525: * @param element The axlDtdElement to be added to the give axlDtd
526: * object.
527: *
528: * @return axl_true if the given axlDtdElement is compatible inside
529: * the axlDtd declaration or axl_false if a error is found.
530: */
531: axl_bool __axl_dtd_add_element (axlDtd * dtd, axlDtdElement * element,
532: axlStream * stream, axlError ** error)
533: {
534: int iterator = 0;
535: axlDtdElement * dtd_element_aux = NULL;
536:
537: /* check that there is no element already named like the
538: * element received. If it is the case drop an error */
539: while (iterator < axl_list_length (dtd->elements)) {
540: dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
541: if (axl_cmp (dtd_element_aux->name, element->name)) {
542: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD element for <%s> == <%s> was defined twice",
543: dtd_element_aux->name, element->name);
544:
545: axl_error_new (-1, "Find that an DTD element was defined twice (no more than one time is allowed)",
546: stream, error);
547: axl_stream_free (stream);
548: return axl_false;
549: }
550:
551: /* update current iterator */
552: iterator++;
553: }
554:
555: /* add the new DTD element to the list */
556: axl_list_add (dtd->elements, element);
557: return axl_true;
558: }
559:
560: /**
561: * @internal
562: *
563: * Internal support function which adds the provided content particule
564: * to the dtd item list received. It also perform all operations
565: * required for the chunk_matched option received.
566: *
567: * In the case the function fails to do its work, it will deallocate
568: * the stream, filling the error received.
569: *
570: * According to the chunk matched value, the function will react
571: * adding the element and configuring current element list.
572: *
573: */
574: axl_bool __axl_dtd_element_content_particule_add (axlDtdElementList * dtd_item_list,
575: char * string_aux,
576: int chunk_matched,
577: axlStream * stream,
578: axlError **error)
579: {
580: axlDtdElementListNode * node;
581:
582: /* check if the item list was creted or not */
583: if (dtd_item_list->itemList == NULL) {
584: dtd_item_list->itemList = axl_list_new (axl_list_always_return_1,
585: (axlDestroyFunc) __destroy_axl_dtd_element_list);
586: }
587:
588: /* create the node to be added */
589: node = __create_axl_dtd_element_list (string_aux, NULL);
590:
591: /* know add the element found */
592: axl_list_add (dtd_item_list->itemList, node);
593:
594: /* set configuration for item repetition */
595: switch (chunk_matched) {
596: case 4:
597: /* one or many times */
598: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one to many repeat pattern: (+)");
599: node->times = ONE_OR_MANY;
600: break;
601: case 5:
602: /* zero or many times */
603: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting zero to many repeat pattern: (*)");
604: node->times = ZERO_OR_MANY;
605: break;
606: case 6:
607: /* zero or one time */
608: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one to one repeat pattern: (?)");
609: node->times = ZERO_OR_ONE;
610: break;
611: default:
612: /* one and only one time */
613: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one and only one repeat pattern: ()");
614: node->times = ONE_AND_ONLY_ONE;
615: }
616:
617: /* return that all is ok */
618: return axl_true;
619: }
620:
621:
622: /**
623: * @internal
624: *
625: * @brief Support function which allows to get current repetition
626: * configuration.
627: *
628: * @param stream The stream where the operation will be performed.
629: *
630: * @return Current configuration read, the function will properly work
631: * if it is called when it is espected to find a content specification
632: * repetition. If not found, the \ref ONE_AND_ONLY_ONE is returned.
633: */
634: AxlDtdTimes __axl_dtd_get_repetition_conf (axlStream * stream)
635: {
636: axl_return_val_if_fail (stream, ONE_AND_ONLY_ONE);
637:
638: if (axl_stream_inspect (stream, "?", 1) > 0) {
639:
640: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '?' repetition conf");
641: /* seems the content specification could appear zero
642: * or one time */
643: return ZERO_OR_ONE;
644:
645: } else if (axl_stream_inspect (stream, "+", 1) > 0) {
646:
647: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '+' repetition conf");
648: /* seems the content specification must appear one up
649: * to many */
650: return ONE_OR_MANY;
651:
652: } else if (axl_stream_inspect (stream, "*", 1) > 0) {
653:
654: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '*' repetition conf");
655: /* seems the content specification could appear zero
656: * up to many */
657: return ZERO_OR_MANY;
658: }
659:
660: /* the content specification must appear */
661: return ONE_AND_ONLY_ONE;
662: }
663:
664: /**
665: * @internal
666: *
667: * Support function which creates a child item list, insert it to the
668: * parent item list received.
669: *
670: * @param parent
671: *
672: * @return
673: */
674: axlDtdElementList * __axl_dtd_create_and_queue (axlDtdElementList * parent)
675: {
676: axlDtdElementList * child;
677: axlDtdElementListNode * node;
678:
679: /* create the DTD item list */
680: child = axl_new (axlDtdElementList, 1);
681:
682: /* make by default the item list to be defined as "not
683: * defined" until the first separator is found */
684: child->type = STILL_UNDEF;
685:
686: /* create a node that */
687: node = __create_axl_dtd_element_list (NULL, child);
688:
689: /* create the parent list reference if weren't */
690: if (parent->itemList == NULL) {
691: parent->itemList = axl_list_new (axl_list_always_return_1,
692: (axlDestroyFunc) __destroy_axl_dtd_element_list);
693: }
694:
695: /* add the node */
696: axl_list_add (parent->itemList, node);
697:
698: /* return the new child created */
699: return child;
700: }
701:
702: /**
703: * @internal
704: *
705: * Updates current chunk readed information to allow perform a better
706: * code after calling this function.
707: *
708: */
709: void __axl_dtd_element_spec_update_chunk_matched (axlStream * stream,
710: int * chunk_matched)
711: {
712: /* check for for sequence or choice characters */
713: if (axl_stream_inspect (stream, ",", 1) > 0) {
714: /* flag that we have found a , (choice)
715: * separator */
716: (*chunk_matched) = 1;
717:
718: } else if (axl_stream_inspect (stream, "|", 1) > 0) {
719: /* flag that we have found a | (sequence)
720: * separator */
721: (*chunk_matched) = 2;
722:
723: } else if (axl_stream_inspect (stream, ")", 1) > 0) {
724: /* flag that we have found a | (sequence)
725: * separator */
726: (*chunk_matched) = 3;
727:
728: } else if (axl_stream_inspect (stream, "+", 1) > 0) {
729: /* flag that we have found a | (sequence)
730: * separator */
731: (*chunk_matched) = 4;
732:
733: } else if (axl_stream_inspect (stream, "*", 1) > 0) {
734: /* flag that we have found a | (sequence)
735: * separator */
736: (*chunk_matched) = 5;
737:
738: } else if (axl_stream_inspect (stream, "?", 1) > 0) {
739: /* flag that we have found a | (sequence)
740: * separator */
741: (*chunk_matched) = 6;
742: }
743:
744: return;
745: }
746:
747: /**
748: * @internal
749: *
750: * Support function to read the content particule separator once the
751: * repeat pattern was found
752: *
753: */
754: axl_bool __axl_dtd_element_spec_update_chunk_matched_for_cp_separator (axlStream * stream,
755: int * chunk_matched)
756: {
757: /* consume previous white spaces */
758: AXL_CONSUME_SPACES (stream);
759:
760: /* check for for sequence or choice characters */
761: if (axl_stream_inspect (stream, ",", 1) > 0) {
762: /* flag that we have found a , (choice)
763: * separator */
764: (*chunk_matched) = 1;
765: return axl_true;
766:
767: } else if (axl_stream_inspect (stream, "|", 1) > 0) {
768: /* flag that we have found a | (sequence)
769: * separator */
770: (*chunk_matched) = 2;
771: return axl_true;
772:
773: } else if (axl_stream_inspect (stream, ")", 1) > 0) {
774: /* flag that we have found a | (sequence)
775: * separator */
776: (*chunk_matched) = 3;
777: return axl_true;
778: }
779:
780: return axl_false;
781: }
782:
783: /**
784: * @internal
785: *
786: * Support function which allows to read the next content particule.
787: */
788: char * __axl_dtd_read_content_particule (axlStream * stream,
789: int * chunk_matched,
790: axlStack * dtd_item_stack,
791: axlError ** error)
792: {
793: char * string_aux;
794:
795: /* read the spec particule stopping when a white space
796: * or other character is found */
797: string_aux = axl_stream_get_until (stream, NULL, chunk_matched, axl_true, 8,
798: /* basic, default delimiters: 0, 1, 2, 3 */
799: " ", ",", "|", ")",
800: /* repetition configuration: 4, 5, 6 */
801: "+", "*", "?",
802: /* new dtd item list being opened: 8 */
803: "(");
804: if (string_aux == NULL) {
805: axl_error_new (-1, "Expected to find a element content specification particule, but it wasn't found",
806: stream, error);
807: axl_stack_free (dtd_item_stack);
808: axl_stream_free (stream);
809: return NULL;
810: }
811:
812: /* check the user doesn't nest item list in a not
813: * proper way */
814: if (*chunk_matched == 8) {
815: axl_error_new (-1, "Found a not proper nesting item list for a DTD element, before using ( a separator must be used (CHOICE: |, SEQUENCE: ,)",
816: stream, error);
817: axl_stack_free (dtd_item_stack);
818: axl_stream_free (stream);
819: return NULL;
820: }
821:
822: /* nullify stream internal reference */
823: axl_stream_nullify (stream, LAST_CHUNK);
824:
825: /* return the content particule found */
826: return string_aux;
827: }
828:
829: /**
830: * @internal
831: *
832: * Support function which reads current <!ELEMENT specification,
833: * configuring it to the received axlDtdElement.
834: *
835: * @param stream The stream where the axlDtdElement spec will be read.
836: *
837: * @param dtd_element The axlDtdElement that will receive the content
838: * spec.
839: *
840: * @param error An optional \ref axlError, where errors will be
841: * reported.
842: *
843: * @return \ref axl_true if the content spec was properly read or \ref
844: * axl_false if not.
845: */
846: axl_bool __axl_dtd_read_element_spec (axlStream * stream, axlDtdElement * dtd_element, axlError ** error)
847: {
848: char * string_aux;
849: axl_bool is_pcdata;
850: int chunk_matched = -1;
851: axlStack * dtd_item_stack;
852: axlDtdElementList * dtd_item_list;
853: axl_bool is_empty;
854:
855:
856: /* create the stack used to control which is
857: * the current context for the items read for
858: * the xml DTD especification (pd, pd2, (pr|po), ..) */
859: dtd_item_stack = axl_stack_new (NULL);
860:
861: /* create the DTD item list */
862: dtd_item_list = axl_new (axlDtdElementList, 1);
863:
864: /* by default, set still undef to change it once a separator
865: * is detected or the function ends. This will help to detect
866: * problems produced by people mixing content element
867: * separators. */
868: dtd_item_list->type = STILL_UNDEF;
869:
870: /* set the content spec list to the dtd element read */
871: dtd_element->list = dtd_item_list;
872:
873: /* push the item created */
874: /* axl_stack_push (dtd_item_stack, dtd_item_list); */
875:
876: /* consume previous white spaces */
877: AXL_CONSUME_SPACES (stream);
878:
879: /* check that the content specification have an ( */
880: if (! (axl_stream_inspect (stream, "(", 1))) {
881: axl_error_new (-1, "Expected to find a element content specification opener \"(\", but it wasn't found",
882: stream, error);
883: axl_stack_free (dtd_item_stack);
884: axl_stream_free (stream);
885: return axl_false;
886: }
887:
888: do {
889: /* consume previous white spaces */
890: AXL_CONSUME_SPACES (stream);
891:
892: /* a new item list have been opened */
893: if (axl_stream_inspect (stream, "(", 1) > 0) {
894:
895: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a DTD item list openining: %d",
896: axl_stack_size (dtd_item_stack));
897:
898: /* a new item list is being defined, we have
899: * to queue current dtd_item_list and create a
900: * new item list */
901: axl_stack_push (dtd_item_stack, dtd_item_list);
902:
903: /* create the DTD item list */
904: dtd_item_list = __axl_dtd_create_and_queue (dtd_item_list);
905:
906: /* let's continue at the begining */
907: continue;
908: }
909:
910:
911: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "iterating again to get a new content particule (item list size: %d)",
912: axl_dtd_item_list_count (dtd_item_list));
913:
914: /* read the next content particule: here is the chunk
915: * matched codes found:
916: * basic, default delimiters:
917: * 0, 1, 2, 3 -> " ", ",", "|", ")"
918: * repetition configuration:
919: * 4, 5, 6 -> "+", "*", "?",
920: * new dtd item list being opened:
921: * 8 -> "(" */
922: string_aux = __axl_dtd_read_content_particule (stream, &chunk_matched, dtd_item_stack, error);
923: if (string_aux == NULL)
924: return axl_false;
925:
926: /* check, and record, that the string read is
927: * PCDATA */
928: is_pcdata = axl_cmp (string_aux, "#PCDATA");
929:
930: /* add the item read if have something defined */
931:
932: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found content spec particule: (size: %d) '%s'",
933: strlen (string_aux),
934: string_aux);
935:
936: /* check if the have matched a white space: next check is
937: * based on the call to axl_stream_get_until at the caller
938: * function: " " */
939: if (chunk_matched == 0) {
940:
941: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
942: "found white spaces as delimiter, consuming them (current chunk matched: %d)",
943: chunk_matched);
944:
945: /* consume previous white spaces */
946: AXL_CONSUME_SPACES (stream);
947:
948: /* update current chunk_matched to conform to
949: * an stream that have all elements really
950: * close: the following function tries to read
951: * and update chunk_matched variable to point
952: * to the value read for ",", "|", "+", "*",
953: * "?" and ")" because white spaces were found */
954: __axl_dtd_element_spec_update_chunk_matched (stream, &chunk_matched);
955:
956:
957: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
958: "current chunk matched before update (%d)",
959: chunk_matched);
960: }
961:
962: /* add the content particule found, this function
963: * already detect that a white space was found and
964: * consumes all white spaces found */
965: if (!__axl_dtd_element_content_particule_add (dtd_item_list, string_aux, chunk_matched, stream, error))
966: return axl_false;
967:
968: if (chunk_matched == 4 || chunk_matched == 5 || chunk_matched == 6) {
969: /* found a repetition pattern */
970: if (! __axl_dtd_element_spec_update_chunk_matched_for_cp_separator (stream, &chunk_matched)) {
971: axl_error_new (-1, "Before a repetition pattern (*,+,?) expected to find a content particule separator",
972: stream, error);
973: axl_stack_free (dtd_item_stack);
974: axl_stream_free (stream);
975: return axl_false;
976: }
977: }
978:
979: /* set current sequence type accoring to separators
980: * used */
981: switch (chunk_matched) {
982: case 1:
983: if (dtd_item_list->type == CHOICE) {
984: axl_error_new (-1, "Detected that the DTD definition is mixing content particules separators at the same level ('|' and ','). First detected a sequence spec (,) but then detected a choice element (|)",
985: stream, error);
986: axl_stack_free (dtd_item_stack);
987: axl_stream_free (stream);
988: return axl_false;
989: }
990: dtd_item_list->type = SEQUENCE;
991: break;
992: case 2:
993: if (dtd_item_list->type == SEQUENCE) {
994: axl_error_new (-1, "Detected that the DTD definition is mixing content particules separators at the same level ('|' and ','). First detected a choice spec (|) but then detected a sequence element (,)",
995: stream, error);
996: axl_stack_free (dtd_item_stack);
997: axl_stream_free (stream);
998: return axl_false;
999: }
1000: dtd_item_list->type = CHOICE;
1001: break;
1002: }
1003:
1004: /* set element type if a element list terminator was
1005: * found ( 3 = ')' = chunk_matched) */
1006: if ((chunk_matched == 3) && is_pcdata) {
1007: if (axl_list_length (dtd_item_list->itemList) == 1)
1008: dtd_element->type = ELEMENT_TYPE_PCDATA;
1009: else if (axl_list_length (dtd_item_list->itemList) > 1)
1010: dtd_element->type = ELEMENT_TYPE_MIXED;
1011: }
1012:
1013: /* pop current element list header */
1014: if (chunk_matched == 3) {
1015: do {
1016: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a DTD item list termination: stack status: %d",
1017: axl_stack_size (dtd_item_stack));
1018: /* consume previous white spaces */
1019: AXL_CONSUME_SPACES (stream);
1020: dtd_item_list->times = __axl_dtd_get_repetition_conf (stream);
1021:
1022: /* consume previous white spaces */
1023: AXL_CONSUME_SPACES (stream);
1024:
1025: if (axl_stream_inspect (stream, ",", 1) > 0) {
1026: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a sequence (,) separator while reading terminator list");
1027:
1028: chunk_matched = 1;
1029: }
1030: else if (axl_stream_inspect (stream, "|", 1) > 0) {
1031: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a choice (|) separator while reading terminator list");
1032: chunk_matched = 2;
1033: }
1034:
1035: /* this means that a ) was found, we have to
1036: * pop current queue */
1037: is_empty = axl_stack_is_empty (dtd_item_stack);
1038: if (! is_empty) {
1039: dtd_item_list = axl_stack_pop (dtd_item_stack);
1040: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting the next item list in the stack, stack status: %d",
1041: axl_stack_size (dtd_item_stack));
1042: }
1043:
1044: /* special case: check if the next element to
1045: * be read is a new ) */
1046: /* consume previous white spaces */
1047: AXL_CONSUME_SPACES (stream);
1048:
1049: }while ((axl_stream_inspect (stream, ")", 1) > 0) && !is_empty);
1050:
1051: /* drop a log */
1052: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "terminator sequence status: chunk matched=%d ans stack status: %d",
1053: chunk_matched, axl_stack_size (dtd_item_stack));
1054:
1055: }
1056:
1057: /* check if we have finished */
1058: } while (chunk_matched != 3 || (! axl_stack_is_empty (dtd_item_stack)));
1059:
1060:
1061: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "content spec terminated, now lookup for the termination");
1062:
1063: /* consume previous white spaces */
1064: /* AXL_CONSUME_SPACES (stream);*/
1065:
1066: /* read here repetition specification */
1067: /* dtd_item_list->times = __axl_dtd_get_repetition_conf (stream); */
1068:
1069: /* set default content element separator */
1070: if (dtd_item_list->type == STILL_UNDEF)
1071: dtd_item_list->type = SEQUENCE;
1072:
1073: /* free the stack used */
1074: axl_stack_free (dtd_item_stack);
1075:
1076:
1077: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD content element specification found and parsed ok");
1078:
1079:
1080: /* content spec readed properly */
1081: return axl_true;
1082: }
1083:
1084: /**
1085: * @internal
1086: *
1087: * Calculates the number of nodes to be matched at minimum for the
1088: * provided DTD element.
1089: *
1090: * @param element The DTD element to configure with its minimum item
1091: * count to be matched.
1092: */
1093: int __axl_dtd_parse_element_get_compulsory_num (axlDtdElementList * list)
1094: {
1095: axlDtdElementListNode * itemNode;
1096: int count = 0;
1097: int iterator = 0;
1098:
1099: /* check for null parameters */
1100: if (list == NULL)
1101: return 0;
1102:
1103: /* only count for repetitiong patterns that makes obligatory
1104: * to have childs */
1105: if (list->times == ONE_AND_ONLY_ONE ||
1106: list->times == ONE_OR_MANY) {
1107:
1108: while (iterator < axl_list_length (list->itemList)) {
1109: /* get the reference for the item node */
1110: itemNode = axl_list_get_nth (list->itemList, iterator);
1111:
1112: /* check if the repetitiong patter is
1113: * compulsory */
1114: if (itemNode->times == ONE_OR_MANY ||
1115: itemNode->times == ONE_AND_ONLY_ONE) {
1116: /* check if we have an itemNode that has an
1117: * Node or a list */
1118: if (itemNode->type == AXL_ELEMENT_NODE) {
1119: /* we have an item node */
1120: count++;
1121: if (list->type == CHOICE) {
1122: /* because we have a
1123: * choice list, once
1124: * validated one item,
1125: * it is the minimum
1126: * requirement. */
1127: return count;
1128: }
1129: } else {
1130: /* we have a list */
1131: count += __axl_dtd_parse_element_get_compulsory_num (itemNode->data);
1132: }
1133: }
1134:
1135: /* update the index */
1136: iterator++;
1137: }
1138: }
1139:
1140: /* return current count */
1141: return count;
1142: }
1143:
1144:
1145: /**
1146: * @internal
1147: *
1148: * Parses a document type element that it is expected to be found at
1149: * the given stream.
1150: *
1151: * @param dtd The axlDtd where the element type readed must be added.
1152: *
1153: * @param stream The stream where the element type if expected to be found.
1154: *
1155: * @param error An axlError, optional, reference where error will be
1156: * reported.
1157: *
1158: * @return axl_true if the element was parsed properly, axl_false if
1159: * not. The stream associated will be unrefered and the axlError
1160: * provided will be filled if an error is found.
1161: */
1162: axl_bool __axl_dtd_parse_element (axlDtd * dtd, axlStream * stream, axlError ** error)
1163: {
1164: char * string_aux;
1165: int matched_chunk = -1;
1166: axlDtdElement * element;
1167:
1168: /* init the dtd element list */
1169: if (dtd->elements == NULL)
1170: dtd->elements = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_element_free);
1171:
1172: /* consume previous white spaces */
1173: AXL_CONSUME_SPACES (stream);
1174:
1175: /* get for the first element declaration */
1176: if (! (axl_stream_inspect (stream, "<!ELEMENT", 9) > 0)) {
1177: axl_error_new (-1, "Expected to receive a <!ELEMENT, but it wasn't found", stream, error);
1178: axl_stream_free (stream);
1179: return axl_false;
1180: }
1181:
1182: /* consume previous white spaces */
1183: AXL_CONSUME_SPACES (stream);
1184:
1185: /* get the element name */
1186: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 3, ">", "(", " ", "<!ELEMENT");
1187: if (string_aux == NULL) {
1188: axl_error_new (-1, "Expected to receive a DTD element name for <!ELEMENT declaration, but not found", stream, error);
1189: axl_stream_free (stream);
1190: return axl_false;
1191: }
1192:
1193: /* check that the DTD have an element name and an element type */
1194: if ((matched_chunk == 0) || (matched_chunk == 3)) {
1195: axl_error_new (-1, "Found a DTD <!ELEMENT declaration, without content specification. Missing value, examples: EMPTY, ANY, (..)", stream, error);
1196: axl_stream_free (stream);
1197: return axl_false;
1198: }
1199:
1200: /* nullify internal stream content */
1201: axl_stream_nullify (stream, LAST_CHUNK);
1202:
1203: /* create the DTD element */
1204: element = axl_new (axlDtdElement, 1);
1205: element->name = string_aux;
1206:
1207: /* consume previous white spaces */
1208: AXL_CONSUME_SPACES (stream);
1209:
1210: /* now, check for the basic cases: ANY and EMPTY */
1211: if (axl_stream_peek (stream, "EMPTY", 5) > 0) {
1212: /* accept previous peek */
1213: axl_stream_accept (stream);
1214:
1215: /* found empty declaration */
1216: element->type = ELEMENT_TYPE_EMPTY;
1217:
1218: } else if (axl_stream_peek (stream, "ANY", 3) > 0) {
1219: /* accept previous peek */
1220: axl_stream_accept (stream);
1221:
1222: /* found any declaration */
1223: element->type = ELEMENT_TYPE_ANY;
1224: } else {
1225: /* complex element type declaration, let's roll now
1226: * get the element content type read current dtd
1227: * element spec.
1228: *
1229: * By default, any comple element type definition,
1230: * have childrens, until PC data definition is found,
1231: * which leads to the two possible values: Mixed and
1232: * PcData */
1233: element->type = ELEMENT_TYPE_CHILDREN;
1234: if (!__axl_dtd_read_element_spec (stream, element, error))
1235: return axl_false;
1236: }
1237:
1238: /* add element found */
1239: if (! __axl_dtd_add_element (dtd, element, stream, error))
1240: return axl_false;
1241:
1242: /* consume previous white spaces */
1243: AXL_CONSUME_SPACES (stream);
1244:
1245: /* check for the last DTD declaration */
1246: if (! (axl_stream_inspect (stream, ">", 1))) {
1247: axl_error_new (-1, "Unable to find last, > terminator for the DTD <!ELEMENT declaration", stream, error);
1248: axl_stream_free (stream);
1249: return axl_false;
1250: }
1251:
1252: /* now, count the number of obligatory elements, required for
1253: * the validation process */
1254: element->minimum_match = __axl_dtd_parse_element_get_compulsory_num (element->list);
1255:
1256: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD element declaration read complete: minimum matching elements: %d",
1257: element->minimum_match);
1258:
1259: /* element type declaration completely read */
1260: return axl_true;
1261: }
1262:
1263: /**
1264: * @internal
1265: *
1266: * Destroy the provided reference and its associated data.
1267: *
1268: * @param decl The reference declaration.
1269: */
1270: void axl_dtd_attribute_decl_free (axlDtdAttributeDecl * decl)
1271: {
1272: /* free the rule name */
1273: if (decl->name != NULL)
1274: axl_free (decl->name);
1275:
1276: /* free the default value */
1277: if (decl->default_value != NULL)
1278: axl_free (decl->default_value);
1279:
1280: /* free enum declaration list if defined */
1281: if (decl->enumvalues != NULL)
1282: axl_list_free (decl->enumvalues);
1283:
1284: /* free the node itself */
1285: axl_free (decl);
1286:
1287: /* nothing more to do */
1288: return;
1289: }
1290:
1291: /**
1292: * @internal function to dealloc an single attribute set decleration.
1293: *
1294: * @param attribute The reference to dealloc.
1295: */
1296: void axl_dtd_attribute_free (axlDtdAttribute * attribute)
1297: {
1298: /* free the attribute list, name and the node itself */
1299: axl_free (attribute->name);
1300: axl_list_free (attribute->list);
1301: axl_free (attribute);
1302:
1303: return;
1304: }
1305:
1306: axl_bool __find_attr_decl (axlPointer _element, axlPointer data)
1307: {
1308: axlDtdAttributeDecl * decl = _element;
1309: char * name = data;
1310:
1311: /* check the name */
1312: if (axl_cmp (decl->name, name))
1313: return axl_true;
1314:
1315: /* it is not the element */
1316: return axl_false;
1317: }
1318:
1319: /**
1320: * @brief Allows to check if the stream contains a reference to a
1321: * entity, calling the resolver to get the replacement text to be
1322: * placed.
1323: *
1324: * @param resolver The function to be called with the replacement
1325: * text. This function must return the replacement text or NULL if it
1326: * fails. Failing to return a reference resolution will make the
1327: * entity reference to appear as is.
1328: *
1329: * @param resolver The entity reference resolver function to be called
1330: * to solve references found.
1331: *
1332: * @param data User defined data provided to the function, passed
1333: * directly to the resolver function once executed.
1334: *
1335: * @param stream The stream where the entity reference could appear.
1336: *
1337: * @param prefix The reference prefix to recognize. Values allowed
1338: * are: % (DTD references) and & (general entity references).
1339: *
1340: * @param error Optional reference to the axlError to report textual
1341: * diagnostic errors.
1342: *
1343: * @return The function return \ref axl_false if some error while
1344: * resolving entity references was found. Otherwise the function
1345: * return axl_true.
1346: */
1347: axl_bool axl_dtd_check_entity_ref_and_expand (axlDtdEntityResolver resolver,
1348: axlPointer data,
1349: axlStream * stream,
1350: const char * prefix,
1351: axlError ** error)
1352:
1353: {
1354: char * string_aux;
1355: char * new_value;
1356: int index;
1357:
1358: /* check if we have an entity reference using the provided prefix */
1359: index = axl_stream_get_index (stream);
1360: if (! (axl_stream_inspect (stream, prefix, 1) > 0))
1361: return axl_true;
1362:
1363: /* get the entity reference until the end */
1364: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, ";");
1365: if (string_aux == NULL) {
1366: axl_error_new (-1, "null value received while expecting to find the entity reference to resolve.", stream, error);
1367: axl_stream_free (stream);
1368: return axl_false;
1369: } /* end if */
1370:
1371: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found entity reference: %s%s;...resolving", prefix, string_aux);
1372:
1373: /* resolve the reference */
1374: new_value = (char *) resolver (string_aux, data);
1375: if (new_value == NULL) {
1376: axl_stream_move (stream, index);
1377: return axl_true;
1378: } /* end if */
1379:
1380: /* accept content consumed */
1381: axl_stream_accept (stream);
1382:
1383: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "entity resolved to: %s", new_value);
1384:
1385: /* place the replacement data at the start of the stream */
1386: new_value = axl_strdup_printf ("%s ", new_value);
1387: axl_stream_push (stream, new_value, strlen (new_value));
1388: axl_free (new_value);
1389:
1390: return axl_true;
1391: }
1392:
1393: /**
1394: * @internal Entity resolver used by __axl_dtd_parse_attlist.
1395: */
1396: const char * __axl_dtd_entity_resolver (const char * entityName, axlPointer data)
1397: {
1398: /* return the entity resolution */
1399: return axl_dtd_entity_value ((axlDtd *) data, entityName, PARAMETER_ENTITY);
1400: } /* end if */
1401:
1402: axlList * __axl_dtd_parse_enumvalues (const char * _enum_values)
1403: {
1404: char ** result;
1405: int iterator;
1406: axlList * list;
1407:
1408: result = axl_stream_split (_enum_values, 1, "|");
1409: iterator = 0;
1410: list = axl_list_new (axl_list_always_return_1, axl_free);
1411:
1412:
1413: while (result[iterator]) {
1414: /* clean the enum value */
1415: axl_stream_trim (result[iterator]);
1416:
1417: /* add to the list */
1418: axl_list_add (list, axl_strdup (result[iterator]));
1419:
1420: /* update the iterator value */
1421: iterator++;
1422:
1423: } /* end while */
1424:
1425: /* free tokens */
1426: axl_stream_freev (result);
1427:
1428: /* return the list */
1429: return list;
1430: }
1431:
1432: /**
1433: * @internal function used by \ref axl_dtd_attr_validation function to
1434: * lookup ATTLIST contraints flaged as unique ID.
1435: */
1436: axl_bool __find_id_decl (axlPointer _element, axlPointer data)
1437: {
1438: /* return the comparision */
1439: return (((axlDtdAttributeDecl *) _element)->type == TOKENIZED_TYPE_ID);
1440:
1441: } /* end __find_id_decl */
1442:
1443:
1444: /**
1445: * @internal
1446: *
1447: * Parse the <!ATTLIST decleration, registering it into the provided
1448: * dtd element.
1449: */
1450: axl_bool __axl_dtd_parse_attlist (axlDtd * dtd, axlStream * stream, axlError ** error)
1451: {
1452: char * string_aux = NULL;
1453: int matched_chunk = -1;
1454: axlDtdAttribute * attribute = NULL;
1455: axlDtdAttributeDecl * decl = NULL;
1456: axlDtdAttributeDecl * declAux = NULL;
1457: char * err_msg;
1458:
1459: /* init the dtd attr list */
1460: if (dtd->attributes == NULL)
1461: dtd->attributes = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_attribute_free);
1462:
1463: /* consume previous white spaces */
1464: AXL_CONSUME_SPACES (stream);
1465:
1466: /* get the element name */
1467: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 1, " ");
1468: if (string_aux == NULL) {
1469: axl_error_new (-1, "Expected to receive a DTD attribute name for <!ATTLIST declaration, but not found", stream, error);
1470: axl_stream_free (stream);
1471: return axl_false;
1472: }
1473:
1474: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found dtd attr declaration for node: <%s>", string_aux);
1475:
1476: /* find the node that holds all attr declarations for the node found */
1477: attribute = axl_dtd_get_attr (dtd, string_aux);
1478:
1479: /* check if found */
1480: if (attribute == NULL) {
1481: /* create the axlDtdAttribute holder */
1482: attribute = axl_new (axlDtdAttribute, 1);
1483:
1484: /* record the node to which the list of rules applies */
1485: axl_stream_nullify (stream, LAST_CHUNK);
1486: attribute->name = string_aux;
1487:
1488: /* init the attribute rule list */
1489: attribute->list = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_attribute_decl_free);
1490:
1491: /* now configure this new attribute inside the dtd */
1492: axl_list_add (dtd->attributes, attribute);
1493: } /* end if */
1494:
1495: /* now get the list of attributes */
1496: while (1) {
1497: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "finding next att declaration");
1498:
1499: /* consume previous white spaces */
1500: AXL_CONSUME_SPACES (stream);
1501:
1502: /* check if we have finished */
1503: if (axl_stream_inspect (stream, ">", 1) > 0)
1504: break;
1505:
1506: /* get the attribute name the rules applies */
1507: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 1, " ");
1508: if (string_aux == NULL) {
1509: axl_error_new (-1, "Expected to receive an attribute name for <!ATTLIST declaration, but not found", stream, error);
1510: axl_stream_free (stream);
1511: return axl_false;
1512: }
1513:
1514: /* nully the string and store it new rule created */
1515: axl_stream_nullify (stream, LAST_CHUNK);
1516:
1517: /* create a new attribute single constraint */
1518: decl = axl_new (axlDtdAttributeDecl, 1);
1519: decl->name = string_aux;
1520:
1521: /* add the attribute constraint to the list */
1522: axl_list_add (attribute->list, decl);
1523:
1524: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "find constraint for attribute name=%s", decl->name);
1525:
1526: /* consume previous white spaces */
1527: AXL_CONSUME_SPACES (stream);
1528:
1529: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking constraint type..");
1530:
1531: /* check for an entity reference and expand the stream
1532: * content with its resolution */
1533: if (! axl_dtd_check_entity_ref_and_expand (__axl_dtd_entity_resolver, dtd, stream, "%", error))
1534: return axl_false;
1535:
1536: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "about to check attr constraint type, stream status: '%s'",
1537: axl_stream_get_following (stream, 30));
1538:
1539: /* now check the contraint type */
1540: if (axl_stream_inspect (stream, "NOTATION", 8) > 0) {
1541: /* parse notation declaration */
1542: }else if (axl_stream_inspect (stream, "(", 1) > 0) {
1543: /* parse enum declaration */
1544: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 1, ")");
1545: if (string_aux == NULL) {
1546: axl_error_new (-1, "expected to find enum declaration but termination caracter ')' was not found", stream, error);
1547: axl_stream_free (stream);
1548: return axl_false;
1549: } /* end if */
1550: decl->type = ENUMERATION_TYPE;
1551: decl->enumvalues = __axl_dtd_parse_enumvalues (string_aux);
1552: }else {
1553: /* set the attribute type */
1554: if (axl_stream_inspect (stream, "CDATA", 5) > 0) {
1555: decl->type = CDATA_ATTRIBUTE;
1556: } else if (axl_stream_inspect (stream, "IDREFS", 6) > 0) {
1557:
1558: /* flag the type */
1559: decl->type = TOKENIZED_TYPE_IDREFS;
1560:
1561: /* flag the dtd to have a IDREF declaration */
1562: dtd->haveIdRefDecl = axl_true;
1563: } else if (axl_stream_inspect (stream, "IDREF", 5) > 0) {
1564: /* notify type found */
1565: decl->type = TOKENIZED_TYPE_IDREF;
1566:
1567: /* flag the dtd to have a IDREF declaration */
1568: dtd->haveIdRefDecl = axl_true;
1569:
1570: } else if (axl_stream_inspect (stream, "ID", 2) > 0) {
1571:
1572: /* notify the type found */
1573: decl->type = TOKENIZED_TYPE_ID;
1574:
1575: /* flag the dtd to have a ID declaration */
1576: dtd->haveIdDecl = axl_true;
1577:
1578: } else if (axl_stream_inspect (stream, "ENTITY", 6) > 0)
1579: decl->type = TOKENIZED_TYPE_ENTITY;
1580: else if (axl_stream_inspect (stream, "ENTITIES", 8) > 0)
1581: decl->type = TOKENIZED_TYPE_ENTITIES;
1582: else if (axl_stream_inspect (stream, "NMTOKENS", 8) > 0)
1583: decl->type = TOKENIZED_TYPE_NMTOKENS;
1584: else if (axl_stream_inspect (stream, "NMTOKEN", 7) > 0)
1585: decl->type = TOKENIZED_TYPE_NMTOKEN;
1586: else {
1587: axl_error_new (-1, "Unrecognied attr type declaration found, check your <!ATTLIST declaration", stream, error);
1588: axl_stream_free (stream);
1589: return axl_false;
1590: } /* end if */
1591: } /* end if */
1592:
1593: /* consume previous white spaces */
1594: AXL_CONSUME_SPACES (stream);
1595:
1596: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking default value declaration, stream status: '%s'",
1597: axl_stream_get_following (stream, 30));
1598:
1599: /* get default declaration value */
1600: if (axl_stream_inspect (stream, "#REQUIRED", 9) > 0) {
1601: decl->defaults = ATT_REQUIRED;
1602: } else if (axl_stream_inspect (stream, "#IMPLIED", 8) > 0) {
1603: decl->defaults = ATT_IMPLIED;
1604: } else {
1605: decl->defaults = ATT_IMPLIED;
1606: if (axl_stream_inspect (stream, "#FIXED", 6) > 0) {
1607: decl->defaults = ATT_FIXED;
1608:
1609: /* consume previous white spaces */
1610: AXL_CONSUME_SPACES (stream);
1611: }
1612:
1613: /* check default value for this case */
1614: if (! (axl_stream_peek (stream, "\"", 1) > 0 ||
1615: axl_stream_peek (stream, "'", 1) > 0)) {
1616: err_msg = axl_strdup_printf ("Unable to find default attribute declaration (#REQUIRED, #IMPLIED, #FIXED) for attribute %s, node <%s>",
1617: decl->name, attribute->name);
1618: axl_error_new (-1, err_msg, stream, error);
1619: axl_stream_free (stream);
1620: axl_free (err_msg);
1621: return axl_false;
1622: } /* end if */
1623: } /* end if */
1624:
1625: /* check constraint for ID types */
1626: if (decl->type == TOKENIZED_TYPE_ID) {
1627: /* check that the node doesn't have any unique
1628: * id declared */
1629:
1630: /* check if the node have TOKENIZED_TYPE_ID */
1631: declAux = axl_list_lookup (attribute->list, __find_id_decl, NULL);
1632: if (declAux != NULL && !axl_cmp (declAux->name, decl->name)) {
1633: err_msg = axl_strdup_printf ("Found ATTLIST declaration, with several ID declarations <ATTLIST %s %s..",
1634: attribute->name, decl->name);
1635: axl_error_new (-1, err_msg, stream, error);
1636: axl_stream_free (stream);
1637: axl_free (err_msg);
1638: return axl_false;
1639: } /* end if */
1640:
1641: /* check required and implied */
1642: if (decl->defaults != ATT_REQUIRED && decl->defaults != ATT_IMPLIED) {
1643: err_msg = axl_strdup_printf ("Found ATTLIST declaration, with ID, that don't have configured either #IMPLICIT or #REQUIRED for attribute %s, node <%s>",
1644: decl->name, attribute->name);
1645: axl_error_new (-1, err_msg, stream, error);
1646: axl_stream_free (stream);
1647: axl_free (err_msg);
1648: return axl_false;
1649: } /* end if */
1650: } /* end if */
1651:
1652: /* consume previous white spaces */
1653: AXL_CONSUME_SPACES (stream);
1654:
1655: /* nullify to check this value later */
1656: string_aux = NULL;
1657: if (axl_stream_inspect (stream, "\"", 1) > 0) {
1658: /* get until */
1659: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "\"");
1660: } else if (axl_stream_inspect (stream, "'", 1) > 0) {
1661: /* get until */
1662: string_aux = axl_stream_get_until (stream, NULL, NULL, axl_true, 1, "\'");
1663: } /* end if */
1664:
1665: /* check if default value was found */
1666: if (string_aux != NULL) {
1667:
1668: /* found default value, check if we have an
1669: * enumeration type, enforcing that the value
1670: * defined to be inside the enumeration */
1671: if (decl->type == ENUMERATION_TYPE) {
1672: if (axl_list_lookup (decl->enumvalues, axl_list_find_string, string_aux) == NULL) {
1673: axl_error_new (-1,
1674: "Configured a default value for an attribute list which only accepts a set of enum values that do not containt it.",
1675: stream, error);
1676: axl_stream_free (stream);
1677: return axl_false;
1678: } /* end if */
1679: } /* end if */
1680:
1681: /* nullify value and make string_aux to be
1682: * owned by the axlDtdAttributeDecl
1683: * reference */
1684: axl_stream_nullify (stream, LAST_CHUNK);
1685: decl->default_value = string_aux;
1686: } /* end if */
1687:
1688: } /* end while */
1689:
1690: /* properly parsed */
1691: return axl_true;
1692: }
1693:
1694: /**
1695: * @internal
1696: *
1697: * Destroy the provided entity reference and all allocated memory.
1698: *
1699: * @param entity The entity the deallocate.
1700: */
1701: void axl_dtd_entity_free (axlDtdEntity * entity)
1702: {
1703: /* the entity reference */
1704: axl_return_if_fail (entity);
1705:
1706: /* free the entity name */
1707: if (entity->name)
1708: axl_free (entity->name);
1709:
1710: /* free the content */
1711: if (entity->content)
1712: axl_free (entity->content);
1713:
1714: /* free external data if defined */
1715: if (entity->data) {
1716: /* free system literal */
1717: if (entity->data->system_literal)
1718: axl_free (entity->data->system_literal);
1719:
1720: /* free public literal */
1721: if (entity->data->public_literal)
1722: axl_free (entity->data->public_literal);
1723:
1724: /* free ndata literal */
1725: if (entity->data->ndata)
1726: axl_free (entity->data->ndata);
1727:
1728: /* free the node itself */
1729: axl_free (entity->data);
1730: }
1731:
1732: /* free the node */
1733: axl_free (entity);
1734:
1735: return;
1736: }
1737:
1738: /**
1739: * @internal
1740: *
1741: * Parses an entity definition from the current status of the stream
1742: * provided.
1743: */
1744: axl_bool __axl_dtd_parse_entity (axlDtd * dtd, axlStream * stream, axlError ** error)
1745: {
1746: char * string_aux;
1747: int matched_chunk;
1748: axlDtdEntity * entity;
1749:
1750: /* init the dtd element list */
1751: if (dtd->entities == NULL)
1752: dtd->entities = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_entity_free);
1753:
1754: /* consume previous white spaces */
1755: AXL_CONSUME_SPACES (stream);
1756:
1757: /* get for the first element declaration */
1758: if (! (axl_stream_inspect (stream, "<!ENTITY", 8) > 0)) {
1759: axl_error_new (-1, "Expected to receive a <!ENTITY, but it wasn't found", stream, error);
1760: axl_stream_free (stream);
1761: return axl_false;
1762: }
1763:
1764: /* consume previous white spaces */
1765: AXL_CONSUME_SPACES (stream);
1766:
1767: /* create a new entity */
1768: entity = axl_new (axlDtdEntity, 1);
1769:
1770: /* set the entity and return axl_true */
1771: axl_list_add (dtd->entities, entity);
1772:
1773: /* check for parameter entity definition */
1774: if (axl_stream_inspect (stream, "%", 1) > 0) {
1775: /* set the entity type */
1776: entity->type = PARAMETER_ENTITY;
1777:
1778: /* consume previous white spaces */
1779: AXL_CONSUME_SPACES (stream);
1780:
1781: } else
1782: entity->type = GENERAL_ENTITY;
1783:
1784: /* get the element name */
1785: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_false, 1, " ");
1786: if (string_aux == NULL) {
1787: axl_error_new (-1, "Expected to receive a DTD entity name for <!ENTITY declaration, but not found", stream, error);
1788: axl_stream_free (stream);
1789: return axl_false;
1790: }
1791:
1792: /* set the name */
1793: axl_stream_nullify (stream, LAST_CHUNK);
1794: entity->name = string_aux;
1795:
1796: /* consume previous white spaces */
1797: AXL_CONSUME_SPACES (stream);
1798:
1799: /* now check if we have an external reference */
1800: if (axl_stream_inspect (stream, "PUBLIC", 6) > 0) {
1801: /* we have a public external resource definition */
1802:
1803: }else if (axl_stream_inspect (stream, "SYSTEM", 6) > 0) {
1804: /* we have a system definition */
1805:
1806: }else {
1807: /* we have a plain value get the content remove next "
1808: and ' if defined */
1809: if (! ((axl_stream_inspect (stream, "\"", 1) > 0))) {
1810: if (! (axl_stream_inspect (stream, "\'", 1) > 0)) {
1811: axl_error_new (-2, "Expected to find entity value initiator (\") or ('), every entity value must start with them",
1812: stream, error);
1813: axl_stream_free (stream);
1814: return axl_false;
1815: }
1816: /* knowing that ' was matched, now get the attribute value */
1817: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 1, "'");
1818: }else {
1819: /* knowhing that " was matched, now get the attribute value */
1820: string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, axl_true, 1, "\"");
1821: }
1822:
1823: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "entity value found: [%s]", string_aux);
1824:
1825: /* nullify internal reference so we have the
1826: * only one reference to entity content value
1827: * inside string_aux */
1828: axl_stream_nullify (stream, LAST_CHUNK);
1829:
1830: /* set the value */
1831: entity->content = string_aux;
1832: }
1833:
1834: /* consume previous white spaces */
1835: AXL_CONSUME_SPACES (stream);
1836:
1837: /* check last item to parse */
1838: if (! (axl_stream_inspect (stream, ">", 1) > 0)) {
1839: axl_error_new (-2, "Expected to find entity definition terminator (>), but it wasn't found",
1840: stream, error);
1841: axl_stream_free (stream);
1842: return axl_false;
1843: }
1844:
1845: return axl_true;
1846: }
1847:
1848:
1849: /**
1850: * @internal
1851: *
1852: * Implements DTD parsing, reading it from a direct buffer, or a file
1853: * path or a file handle.
1854: */
1855: axlDtd * __axl_dtd_parse_common (const char * entity, int entity_size,
1856: const char * file_path, int fd_handle,
1857: axlError ** error)
1858: {
1859: axlStream * stream;
1860: axlDtd * dtd;
1861: int iterator;
1862:
1863: /* create the stream associated */
1864: stream = axl_stream_new (entity, entity_size, file_path, fd_handle, error);
1865: axl_return_val_if_fail (stream, NULL);
1866:
1867: dtd = __axl_dtd_new ();
1868: axl_stream_link (stream, dtd, (axlDestroyFunc) axl_dtd_free);
1869:
1870: iterator = 0;
1871: while (axl_stream_remains (stream)) {
1872: /* get rid from comments found */
1873: if (! axl_doc_consume_comments (NULL, stream, error))
1874: return NULL;
1875:
1876: /* check for element declaration */
1877: if (axl_stream_peek (stream, "<!ELEMENT", 9) > 0) {
1878: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD element declaration");
1879: /* found element declaration */
1880: if (! __axl_dtd_parse_element (dtd, stream, error))
1881: return NULL;
1882:
1883: continue;
1884:
1885: }
1886:
1887: /* check for attribute list declarations */
1888: if (axl_stream_inspect (stream, "<!ATTLIST", 9) > 0) {
1889: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD attribute list declaration");
1890:
1891: /* parse it */
1892: if (! __axl_dtd_parse_attlist (dtd, stream, error))
1893: return NULL;
1894:
1895: continue;
1896: }
1897:
1898: /* check for the entity declaration */
1899: if (axl_stream_peek (stream, "<!ENTITY", 8) > 0) {
1900: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD entity declaration");
1901:
1902: /* parse the entity definition */
1903: if (! __axl_dtd_parse_entity (dtd, stream, error))
1904: return NULL;
1905:
1906: continue;
1907: }
1908:
1909: /* stop the loop */
1910: if (iterator == 3) {
1911: axl_error_new (-1, "unable to process DTD content, unable to find expected information (no <!ELEMENT, <!ATTLIST or <!ENTITY declaration)", stream, error);
1912: axl_stream_free (stream);
1913: return NULL;
1914: }
1915: iterator++;
1916: }
1917:
1918:
1919: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD elements totally loaded, building references..");
1920:
1921: /* update current root reference, the DTD root for the DTD
1922: * document already parsed */
1923: if (dtd->elements != NULL)
1924: dtd->root = __axl_dtd_get_new_root (dtd);
1925:
1926: /* check if the DTD has ID declarations if found IDREF
1927: * declarations */
1928: if (! dtd->haveIdDecl && dtd->haveIdRefDecl) {
1929: axl_error_new (-1, "DTD semantic error, found IDREF attribute declaration but no attribute ID declaration was found.", stream, error);
1930: axl_stream_free (stream);
1931: return NULL;
1932: }
1933:
1934: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD load COMPLETE");
1935:
1936: axl_stream_unlink (stream);
1937: axl_stream_free (stream);
1938: return dtd;
1939: }
1940:
1941: /**
1942: * @brief Allows to parse the provided entity, which is expected to
1943: * contain a DTD (Document Type Definition).
1944: *
1945: * @param entity The document type definition to parse.
1946: *
1947: * @param entity_size The document size, or -1 to make the function to
1948: * figure out current size.
1949: *
1950: * @param error An optional \ref axlError where errors will be reported.
1951: *
1952: * @return A newly allocated \ref axlDtd that must be deallocated when
1953: * no longer need with \ref axl_dtd_free. The function could return
1954: * NULL on failure detected. On that case, it is requred to check \ref
1955: * axlError variable, if defined.
1956: */
1957: axlDtd * axl_dtd_parse (const char * entity,
1958: int entity_size,
1959: axlError ** error)
1960: {
1961:
1962: return __axl_dtd_parse_common (entity, entity_size, NULL, -1, error);
1963: }
1964:
1965: /**
1966: * @brief Allows to parse the provided DTD definition, which is found
1967: * on the provided file path.
1968: *
1969: * @param file_path The file path where it is expected to receive a
1970: * DTD file.
1971: *
1972: * @param error An optional \ref axlError reference where all errors found will be reported.
1973: *
1974: * @return A newly allocated \ref axlDtd instance or NULL if it fails.
1975: *
1976: * <b>Making a DTD to be inline loaded: </b><br>
1977: *
1978: * It may be helpful to make the DTD definition available at your
1979: * binary, inline compiled, to avoid distributing DTD files along with
1980: * libraries, etc. This also solves installation problems like
1981: * provisioning a default location to make your application to find
1982: * such files.
1983: *
1984: * With the following command you can create an inline representation
1985: * from your DTD file:
1986: * \code
1987: * >> axl-knife --input your-file.dtd --dtd-to-c --output your-file.dtd.h --ifnewer
1988: * \endcode
1989: *
1990: * This will create a header with an C-macro style definition of your
1991: * DTD. Now, you can include it using:
1992: *
1993: * \code
1994: * #include <your-file.dtd.h>
1995: * \endcode
1996: *
1997: * In the case you are developing a library, it is recommended to do
1998: * such include at the body implementation (usually .c or .cpp files,
1999: * to avoid requiring your API consumers to also include your DTD
2000: * inline definition).
2001: *
2002: * Now, to load your DTD file, use the following:
2003: *
2004: * \code
2005: * axlError * err = NULL;
2006: * axlDtd * dtd = axl_dtd_parse (YOUR_FILE_DTD, -1, &err);
2007: * if (dtd == NULL) {
2008: * // This won't happen unless axl-runtime error found, since axl-knife
2009: * // checks your dtd file before producing the in-line definition.
2010: * // However, bug happens! check this.
2011: * }
2012: * \endcode
2013: */
2014: axlDtd * axl_dtd_parse_from_file (const char * file_path,
2015: axlError ** error)
2016: {
2017: return __axl_dtd_parse_common (NULL, -1, file_path, -1, error);
2018: }
2019:
2020:
2021: /**
2022: * @internal
2023: *
2024: * Support function for axl_dtd_validate which checks if the provided
2025: * parent have its childs configuration according to the values
2026: * expresed on the sequenced represented by the itemList.
2027: *
2028: * The function return axl_true if the validation was ok, or axl_false
2029: * if something have failed. It also creates an error, using the
2030: * optional axlError reference received.
2031: */
2032: axl_bool __axl_dtd_validate_sequence (axlNode * parent,
2033: int * child_position,
2034: axlDtdElementList * itemList,
2035: axlError ** error,
2036: axl_bool try_match,
2037: axl_bool top_level)
2038: {
2039: int iterator = 0;
2040: int child_pos = *child_position;
2041: axlNode * node;
2042: axlDtdElementListNode * itemNode;
2043: axl_bool status = axl_false;
2044: axl_bool one_matched;
2045: AxlDtdTimes times;
2046:
2047:
2048: axl_return_val_if_fail (parent, axl_false);
2049: axl_return_val_if_fail (itemList, axl_false);
2050:
2051:
2052: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validating a sequence list: iterator=%d, item list count=%d, at child position=%d",
2053: iterator, axl_dtd_item_list_count (itemList), child_pos);
2054:
2055: /* iterate over the sequence, checking its order */
2056: while (iterator < axl_dtd_item_list_count (itemList)) {
2057:
2058: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting next item node from the DTD item list at: %d",
2059: iterator);
2060:
2061: /* get the item node specification */
2062: itemNode = axl_dtd_item_list_get_node (itemList, iterator);
2063: one_matched = axl_false;
2064: times = axl_dtd_item_node_get_repeat (itemNode);
2065:
2066: do {
2067:
2068: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting node at child position: %d",
2069: child_pos);
2070:
2071: /* get the node that is located at the same position
2072: * than the sequence */
2073: if (child_pos < axl_node_get_child_num (parent)) {
2074: node = axl_node_get_child_nth (parent, child_pos);
2075: } else
2076: node = NULL;
2077:
2078: /* the node child list have ended, check if
2079: * this situation was expected */
2080: if (node == NULL) {
2081: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "no more child nodes to validate at %d, for parent: %s, times: %d, iterator: %d, item count: %d",
2082: child_pos, axl_node_get_name (parent), times,
2083: iterator, axl_dtd_item_list_count (itemList));
2084: /* check if we were working with a
2085: * list, which have matched at least
2086: * one item */
2087: if (times == ONE_OR_MANY && one_matched && status) {
2088: if ((iterator + 1) == axl_dtd_item_list_count (itemList)) {
2089: *child_position = child_pos;
2090: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (III): %d", child_pos);
2091: return axl_true;
2092: }
2093:
2094: /* reached this point we have
2095: matched a one to many with
2096: at least one match */
2097: break;
2098: }
2099:
2100: /* check that the rest of the
2101: * specification item is optional,
2102: * including the one used */
2103: status = axl_true;
2104: do {
2105: if (times != ZERO_OR_MANY &&
2106: times != ZERO_OR_ONE) {
2107:
2108: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found item, inside the DTD item list, that is not optional: %d (repeat value: %d)",
2109: iterator, times);
2110: status = axl_false;
2111: break;
2112: }
2113:
2114: /* update index and get the next item */
2115: iterator++;
2116: if (iterator < axl_dtd_item_list_count (itemList))
2117: itemNode = axl_dtd_item_list_get_node (itemList, iterator);
2118: }while (status && (iterator < axl_dtd_item_list_count (itemList)));
2119:
2120: /* check status before checking the rest of the item spec */
2121: if (status) {
2122: *child_position = child_pos;
2123:
2124: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (II): %d", child_pos);
2125:
2126: return axl_true;
2127: }
2128:
2129: /* check if a try match is being runned */
2130: if (! try_match) {
2131: axl_error_report (error, -1 ,
2132: "Found that DTD specifies more nodes to be hold by the parent (<%s>), but no more childs were found",
2133: axl_node_get_name (parent));
2134: }
2135:
2136: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found that no nodes left to satisfy DTD validation operation");
2137: *child_position = child_pos;
2138: return axl_false;
2139: }
2140:
2141: /* check node type */
2142: if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_LIST) {
2143:
2144: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the item node is an item list, dtd item list position: %d, child position: %d=<%s>",
2145: iterator, child_pos, axl_node_get_name (node));
2146:
2147: /* element list found, validate its content */
2148: if (! __axl_dtd_validate_item_list (axl_dtd_item_node_get_list (itemNode),
2149: parent, &child_pos, error, axl_false)) {
2150: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sub item list validation have failed (not critical)");
2151: /* check if we are the top
2152: * level list and the itemNode
2153: * checked is the last one
2154: * item on the item list */
2155: if (top_level && ((iterator + 1) == axl_node_get_child_num (parent))) {
2156: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the last item list wasn't matched");
2157:
2158: }
2159:
2160: *child_position = child_pos;
2161: return axl_false;
2162: }
2163:
2164: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validated item list, child position after: %d",
2165: child_pos);
2166: /* because child position updating and
2167: * repeat matching is already handled
2168: * by dtd_validate_item_list function
2169: * we just continue with the next
2170: * iteration */
2171: break;
2172:
2173: } else if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_NODE) {
2174: /* check the name against the spec */
2175:
2176: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
2177: "the item node is a final content particule definition: %s",
2178: axl_dtd_item_node_get_value (itemNode));
2179:
2180: status = NODE_CMP_NAME (node, axl_dtd_item_node_get_value (itemNode));
2181: }
2182:
2183: /* check previous status */
2184: if ((times == ONE_AND_ONLY_ONE) ||
2185: (times == ONE_OR_MANY && one_matched == axl_false)) {
2186: if (! status) {
2187: /* only report an upper level
2188: * error if we are not running
2189: * a try match */
2190: if (! try_match) {
2191:
2192: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL,
2193: "Found different node (<%s>) for a sequence expected (<%s>), at child position: %d, item list pos: %d",
2194: axl_node_get_name (node),
2195: axl_dtd_item_node_get_value (itemNode),
2196: child_pos, iterator);
2197: axl_error_report (error, -1,
2198: "Found different node (<%s>) for a sequence expected (<%s>), at child position: %d, item list pos: %d",
2199: axl_node_get_name (node),
2200: axl_dtd_item_node_get_value (itemNode),
2201: child_pos, iterator);
2202: }
2203: /* return that a match wasn't possible */
2204: *child_position = child_pos;
2205: return axl_false;
2206: }
2207: }
2208:
2209: /* according to the repetition pattern, update loop indexes */
2210: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "updating child nodes references: %d, repeat type: %d, status=%d",
2211: child_pos, times, status);
2212:
2213:
2214: /* one only item to match and exactly one */
2215: if (times == ONE_AND_ONLY_ONE) {
2216: child_pos++;
2217: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "updated child position to: %d, repeat type: %d, status=%d",
2218: child_pos, times, status);
2219: break;
2220: }
2221:
2222: /* one or many items to match */
2223: if (times == ONE_OR_MANY) {
2224:
2225: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched one to many item node: status=%d one_matched=%d",
2226: status, one_matched);
2227:
2228: /* if the match have failed and
2229: * previous matches was ok, it seems
2230: * we have reached the next
2231: * items. Just break the loop */
2232: if (status == axl_false && one_matched == axl_true)
2233: break;
2234:
2235: child_pos++;
2236: one_matched = axl_true;
2237: continue; /* don't break the loop */
2238: }
2239:
2240: /* zero or optionally one item to match */
2241: if (times == ZERO_OR_ONE) {
2242: /* if previous status was ok, it seems
2243: * that we have matched the optional
2244: * character. In that case, move the
2245: * index to the following value. If
2246: * not, just break the loop. */
2247: if (status == axl_true)
2248: child_pos++;
2249: break;
2250: }
2251:
2252: /* zero or many items to match */
2253: if (times == ZERO_OR_MANY) {
2254: if (status == axl_true) {
2255: one_matched = axl_true;
2256: child_pos++;
2257: continue;
2258: }
2259: break;
2260: }
2261:
2262:
2263: /* until break the loop */
2264: }while (axl_true);
2265:
2266: /* update iterator index */
2267: iterator++;
2268: }
2269:
2270: /* check if more nodes where specified than the DTD spec */
2271: times = axl_dtd_item_list_repeat (itemList);
2272: if ((times == ONE_OR_MANY || times == ONE_AND_ONLY_ONE) &&
2273: top_level && (child_pos < axl_node_get_child_num (parent))) {
2274:
2275: /* drop a log */
2276: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> have more childs=%d than the childs iterated=%d, top_level=%d",
2277: axl_node_get_name (parent),
2278: axl_node_get_child_num (parent),
2279: child_pos, top_level);
2280:
2281: /* do not report an error found if a try match is
2282: * being run */
2283: if (! try_match) {
2284: axl_error_new (-1, "More childs, than the ones especified in the DTD, were found",
2285: NULL, error);
2286: }
2287: /* return that the match wasn't possible */
2288: *child_position = child_pos;
2289: return axl_false;
2290: }
2291:
2292: /* return that the sequence has been validated */
2293: *child_position = child_pos;
2294:
2295: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (I): %d", child_pos);
2296:
2297: return axl_true;
2298: }
2299:
2300: /**
2301: * @internal
2302: *
2303: * Internal support function to validate the choice list.
2304: */
2305: axl_bool __axl_dtd_validate_choice (axlNode * parent,
2306: int * child_position,
2307: axlDtdElementList * itemList,
2308: axlError ** error,
2309: axl_bool try_match,
2310: axl_bool top_level)
2311: {
2312: axlNode * node;
2313: axlDtdElementListNode * itemNode;
2314: int iterator;
2315: axl_bool status;
2316: AxlDtdTimes times;
2317: axl_bool one_match;
2318:
2319:
2320: if (*child_position < axl_node_get_child_num (parent)) {
2321: /* get a reference to be matched by the choice list */
2322: node = axl_node_get_child_nth (parent, *child_position);
2323: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validated choice list at position: %d=<%s>", *child_position, axl_node_get_name (node));
2324: } else {
2325: /* tried to match a choice list with a child index
2326: * outside the maximum number of childs */
2327: if (! try_match) {
2328: axl_error_new (-1, "Unable to match choice list, it seems that the are not enough childs to validate the choice list",
2329: NULL, error);
2330: }
2331: return axl_false;
2332: }
2333:
2334: iterator = 0;
2335: while (iterator < axl_dtd_item_list_count (itemList)) {
2336: /* get the DTD item list to match */
2337: itemNode = axl_dtd_item_list_get_node (itemList, iterator);
2338: times = axl_dtd_item_node_get_repeat (itemNode);
2339:
2340: if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_NODE) {
2341: /* reset match configuration */
2342: one_match = axl_false;
2343: repeat_for_node:
2344: /* a node was found */
2345: status = NODE_CMP_NAME (node, axl_dtd_item_node_get_value (itemNode));
2346:
2347: /* know, if the node was matched check it
2348: * repetition configuration */
2349: if (status) {
2350: /* update child position */
2351: (*child_position)++;
2352:
2353: if (times == ONE_AND_ONLY_ONE || times == ZERO_OR_ONE) {
2354: /* the node was matched and
2355: * the itemNode has a one and
2356: * only one configuration,
2357: * just return that the choice
2358: * list was matched */
2359: return axl_true;
2360: }
2361: if (times == ONE_OR_MANY || times == ZERO_OR_MANY) {
2362: /* because the node was matched, but the repetition
2363: * pattern allows to match more nodes we have to
2364: * iterate a bit more */
2365: node = axl_node_get_child_nth (parent, *child_position);
2366: if (node == NULL) {
2367: /* because we already matched at least one item,
2368: * we can assume that the itemNode was successfully
2369: * matched for both cases (*) and (+). */
2370: return axl_true;
2371: }
2372: /* flag the one match */
2373: one_match = axl_true;
2374:
2375: /* if the node reference is
2376: * not NULL, try to match the
2377: * next item */
2378: goto repeat_for_node;
2379: }
2380: } /* end if */
2381:
2382: /* before returning, that that we have matched
2383: * previously, at least, one node for
2384: * one-to-many and zero-to-many pattern */
2385: if ((times == ONE_OR_MANY || times == ZERO_OR_MANY) && one_match) {
2386: return axl_true;
2387: }
2388:
2389: } else if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_LIST) {
2390: /* an element list was found, call to validate it */
2391: /* element list found, validate its content */
2392: if (__axl_dtd_validate_item_list (axl_dtd_item_node_get_list (itemNode),
2393: parent, child_position, error, axl_false)) {
2394: /* item list matched */
2395: return axl_true;
2396: }
2397: }
2398:
2399: /* no item was matched, update iterator indexes */
2400: iterator++;
2401: }
2402:
2403: /* seems that the choice list wasn't matched */
2404: if (! try_match) {
2405: axl_error_new (-1, "Unable to match choice list, after checking all posibilities, choice list wasn't validated",
2406: NULL, error);
2407: }
2408: return axl_false;
2409: }
2410:
2411: /**
2412: * @internal
2413: *
2414: * Tries to perform a validation, based on the item list received and
2415: * the repetition configuration.
2416: *
2417: * @param itemList The item list containing DTD content spec
2418: * information used to validate.
2419: *
2420: * @param parent The parent node where the validation process is being
2421: * applied. The content spec refers to the childs the parent has.
2422: *
2423: * @param stack An stack used by the overall process to store the
2424: * subsequent parents to be validated. This stack must be released if
2425: * a error is found.
2426: *
2427: * @param error An optional axlError reference containing the error
2428: * textual diagnostic if found.
2429: *
2430: * @return axl_true if the validation was ok, otherwise axl_false is
2431: * returned.
2432: */
2433: axl_bool __axl_dtd_validate_item_list (axlDtdElementList * itemList,
2434: axlNode * parent,
2435: int * child_position,
2436: axlError ** error,
2437: axl_bool top_level)
2438: {
2439: int temp_child_pos;
2440: axl_bool status;
2441: axl_bool already_matched;
2442:
2443:
2444: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validating an item list with repeat pattern: %d, at %d, top level=%d",
2445: axl_dtd_item_list_repeat (itemList), *child_position, top_level);
2446:
2447: /* now check repetition type */
2448: switch (axl_dtd_item_list_repeat (itemList)) {
2449: case ONE_AND_ONLY_ONE:
2450: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (one and only one) spec..");
2451: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2452:
2453: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a SEQUENCE form");
2454: /* it is a choice, so the item list specifies
2455: * the nodes that could appear */
2456: if (!__axl_dtd_validate_sequence (parent, child_position, itemList, error,
2457: axl_false, top_level)) {
2458: return axl_false;
2459: }
2460: }else {
2461: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a CHOICE form");
2462: /* it is a sequence, so, item list
2463: * specification represents the nodes, in the
2464: * order they must appear */
2465: if (!__axl_dtd_validate_choice (parent, child_position, itemList, error,
2466: axl_false, top_level)) {
2467: return axl_false;
2468: }
2469: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "choice list was properly validated");
2470: }
2471: break;
2472: case ZERO_OR_ONE:
2473:
2474: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or one) spec..");
2475: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2476:
2477: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a SEQUENCE form, parent: <%s>",
2478: axl_node_get_name (parent));
2479: /* because we are running a zero or one item
2480: * list matching, we don't care if it doesn't
2481: * match. In the case it match, the child
2482: * position is updated and next calls will be
2483: * properly aligned. In the match doesn't
2484: * happens, it also don't matter because the
2485: * pattern allow to not match */
2486: temp_child_pos = *child_position;
2487: if (!__axl_dtd_validate_sequence (parent, child_position, itemList, error,
2488: axl_true, top_level)) {
2489: /* check that the match wasn't
2490: * produced, at any level */
2491: if (temp_child_pos != *child_position) {
2492: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch (%d != %d)",
2493: temp_child_pos, *child_position);
2494: axl_error_new (-1, "Found an DTD item list definition, that should be matched entirely or not, zero or one time, but it was matched partially",
2495: NULL, error);
2496: return axl_false;
2497: }
2498:
2499: return axl_false;
2500: }
2501:
2502: }else {
2503: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
2504:
2505: /* it is a sequence, so, item list
2506: * specification represents the nodes, in the
2507: * order they must appear */
2508: __axl_dtd_validate_choice (parent, child_position, itemList, error,
2509: axl_true, top_level);
2510: }
2511: break;
2512: case ZERO_OR_MANY:
2513:
2514: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or many) spec..");
2515: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2516:
2517: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a SEQUENCE (size: %d) form ",
2518: axl_dtd_item_list_count (itemList));
2519: /* one this case, several matches must be
2520: * tried, until the validation fails */
2521: do {
2522: temp_child_pos = *child_position;
2523: status = __axl_dtd_validate_sequence (parent, child_position, itemList, error,
2524: axl_true, top_level);
2525:
2526: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence match status=%d", status);
2527: if (! status) {
2528: /* check that the match wasn't
2529: * produced, at any level */
2530: if ((temp_child_pos != *child_position)) {
2531:
2532: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch (%d != %d)",
2533: temp_child_pos, *child_position);
2534: axl_error_new (-1, "Found an DTD item list definition, that should be matched entirely or not, zero or many times, but it was matched partially",
2535: NULL, error);
2536: return axl_false;
2537: }
2538: }
2539: }while (status);
2540: }else {
2541:
2542: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
2543: /* it is a sequence, so, item list
2544: * specification represents the nodes, in the
2545: * order they must appear */
2546: do {
2547: status = __axl_dtd_validate_choice (parent, child_position, itemList, error,
2548: axl_true, top_level);
2549: }while (status);
2550: }
2551: break;
2552: case ONE_OR_MANY:
2553: /* one or many sequence spec (+) */
2554: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or many) spec..");
2555: if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2556:
2557: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a SEQUENCE (size: %d) form ",
2558: axl_dtd_item_list_count (itemList));
2559:
2560: /* one this case, several matches must be
2561: * tried, until the validation fails */
2562: already_matched = axl_false;
2563: do {
2564: temp_child_pos = *child_position;
2565: /* try to match the one or many
2566: sequence according to the value
2567: stored inside already matched */
2568: status = __axl_dtd_validate_sequence (parent, child_position, itemList, error,
2569: already_matched, top_level);
2570:
2571: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence match status=%d", status);
2572: if (! status) {
2573: /* check that the match wasn't
2574: * produced, at any level */
2575: if ((temp_child_pos != *child_position)) {
2576:
2577: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch, matched partially (%d != %d)",
2578: temp_child_pos, *child_position);
2579: axl_error_new (-1,
2580: "Found an DTD item list definition, that should be matched entirely or not, one or many times, but it was matched partially",
2581: NULL, error);
2582: return axl_false;
2583: }
2584: }else {
2585: /* set that we have matched, at least, one item */
2586: already_matched = axl_true;
2587: }
2588:
2589:
2590:
2591: }while (status);
2592: }else {
2593:
2594: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
2595: /* it is a sequence, so, item list
2596: * specification represents the nodes, in the
2597: * order they must appear */
2598: already_matched = axl_false;
2599: do {
2600: /* the next choice matching is done
2601: * according to the value stored in
2602: * already matched */
2603: status = __axl_dtd_validate_choice (parent, child_position, itemList, error,
2604: already_matched, top_level);
2605: /* if the validation successed, set
2606: * that next matched are not required
2607: * to be successful ones */
2608: if (status)
2609: already_matched = axl_true;
2610: }while (status);
2611: }
2612: break;
2613: default:
2614: /* this case will never be reached */
2615: #define INTERNAL_ERROR_01 "critical error reached a place that shows the dtd parser is not properly defining the repetition pattern for the current itemList."
2616: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, INTERNAL_ERROR_01);
2617: axl_error_new (-1, INTERNAL_ERROR_01, NULL, error);
2618: return axl_false;
2619: }
2620:
2621:
2622: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validate item list terminated, now check post-conditions, top_level=%d, item list type=%d, child pos=%d, childs num=%d",
2623: top_level, axl_dtd_item_list_type (itemList), *child_position, axl_node_get_child_num (parent));
2624:
2625: /* check that, in the case that the choice item list is being
2626: * validated, ensure it has validated all nodes, especially if
2627: * we are the top level definition */
2628: if (top_level && (axl_dtd_item_list_type (itemList) == CHOICE)) {
2629: if (((*child_position) + 1) < axl_node_get_child_num (parent)) {
2630:
2631:
2632: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the choice list didn't cover all childs (%d), while parent=<%s> has: (%d)",
2633: (*child_position), axl_node_get_name (parent), axl_node_get_child_num (parent));
2634: axl_error_new (-1, "Found that the validation process didn't cover all nodes, while using a choice list. This means that the xml document have more content than the DTD spec",
2635: NULL, error);
2636: return axl_false;
2637: }
2638: }
2639:
2640: /* element type children validated */
2641: return axl_true;
2642: }
2643:
2644: /**
2645: * @internal
2646: *
2647: * Support function validate parent nodes which are element type
2648: * children ones.
2649: */
2650: axl_bool __axl_dtd_validate_element_type_children (axlDtdElement * element,
2651: axlNode * parent,
2652: axl_bool top_level,
2653: axlError ** error)
2654: {
2655: axlDtdElementList * itemList;
2656: int child_pos = 0;
2657: char * err_msg;
2658:
2659: /* get a reference to the item list */
2660: itemList = axl_dtd_get_item_list (element);
2661:
2662: /* check for xml nodes with fewer content than the initially
2663: * expected. */
2664: if (axl_node_get_child_num (parent) < element->minimum_match) {
2665: err_msg = axl_strdup_printf ("Found that the parent node (<%s>) received doesn't contains enough xml nodes inside to get a proper validation (childs found (%d) != childs that should be found (%d)). This means that the xml document have fewer content than the DTD spec.",
2666: axl_node_get_name (parent),
2667: axl_node_get_child_num (parent),
2668: element->minimum_match);
2669: axl_error_new (-1, err_msg, NULL, error);
2670: axl_free (err_msg);
2671: return axl_false;
2672: }
2673:
2674: /* validate the item list, starting from the child 0 */
2675: if (__axl_dtd_validate_item_list (itemList, parent, &child_pos, error, top_level)) {
2676: /* check if, at least, all minimum elements was
2677: * matched */
2678: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking minimum node match: (%d < min: %d) (%d < childs: %d) node=<%s>",
2679: child_pos, element->minimum_match,
2680: child_pos, axl_node_get_child_num (parent),
2681: axl_node_get_name (parent));
2682:
2683: if (child_pos < axl_node_get_child_num (parent)) {
2684: axl_error_report (error, -1,
2685: "Found that the validation process didn't cover all nodes (%d < min:%d) (%d < childs:%d). All xml child nodes inside the parent=<%s> wasn't covered. This means that the xml document have more content than the DTD spec defines.",
2686: child_pos, element->minimum_match, child_pos, axl_node_get_child_num (parent),
2687: axl_node_get_name (parent));
2688: return axl_false;
2689: }
2690: /* seems that the minimum match */
2691: return axl_true;
2692: }
2693:
2694: return axl_false;
2695: }
2696:
2697: /**
2698: * @internal
2699: * Internal support function to validate #PCDATA nodes.
2700: */
2701: axl_bool __axl_dtd_validate_element_type_pcdata (axlDtdElement * element,
2702: axlNode * parent,
2703: axlStack * stack,
2704: axlError ** error)
2705: {
2706: /* check for childs */
2707: if (axl_node_have_childs (parent)) {
2708: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "node <%s> should be #PCDATA and it contains childs",
2709: axl_node_get_name (parent));
2710: axl_error_new (-1,
2711: "Found a node for which its espeficiation makes it to be a node with only data and no childs, and it currently contains childs",
2712: NULL, error);
2713: return axl_false;
2714: }
2715:
2716: /* return that the validation was ok */
2717: return axl_true;
2718: }
2719:
2720: /**
2721: * @internal
2722: *
2723: * Support function to validate empty nodes.
2724: */
2725: axl_bool __axl_dtd_validate_element_type_empty (axlDtdElement * element,
2726: axlNode * parent,
2727: axlStack * stack,
2728: axlError ** error)
2729: {
2730: char * err_msg;
2731:
2732: /* check the node is indeed, empty */
2733: if (! axl_node_is_empty (parent)) {
2734: err_msg = axl_strdup_printf (
2735: "Found a node <%s> that it is especified that must be empty, but it isn't",
2736: axl_node_get_name (parent));
2737: axl_error_new (-1, err_msg, NULL, error);
2738: axl_free (err_msg);
2739: return axl_false;
2740: }
2741:
2742: /* check the node doesn't have childs */
2743: if (axl_node_have_childs (parent)) {
2744: err_msg = axl_strdup_printf (
2745: "Found a node <%s> that it is especified that must be empty, but it has childs",
2746: axl_node_get_name (parent));
2747: axl_error_new (-1, err_msg, NULL, error);
2748: axl_free (err_msg);
2749: return axl_false;
2750: }
2751:
2752: /* return that the validation was ok */
2753: return axl_true;
2754: }
2755:
2756: axl_bool __axl_dtd_attr_validate_foreach (const char * key, const char * value, axlPointer data, axlPointer data2)
2757: {
2758: axlDtdAttribute * attribute = data;
2759: axlError ** error = data2;
2760: axlDtdAttributeDecl * decl;
2761: char * err_msg;
2762:
2763: /* get declaration associated */
2764: decl = axl_list_lookup (attribute->list, __find_attr_decl, (axlPointer) key);
2765:
2766: if (decl == NULL) {
2767: /* found an error */
2768: err_msg = axl_strdup_printf ("Found an attribute (%s) which is not specified by the attribute declaration for <%s>",
2769: key, attribute->name);
2770: axl_error_new (-1, err_msg, NULL, error);
2771:
2772: /* free the cursor and the error message */
2773: axl_free (err_msg);
2774:
2775: /* return axl_true here because we want to stop the process */
2776: return axl_true;
2777:
2778: } /* end if */
2779:
2780: /* if the declaration is found, now check its
2781: * contraints */
2782: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking contraint for attribute: %s=%s", decl->name, value);
2783: if (decl->type == CDATA_ATTRIBUTE) {
2784: /* found free attribute declaration, go ahed */
2785: } else if (decl->type == ENUMERATION_TYPE) {
2786: /* found an enumeration type, check and return */
2787: if (axl_list_lookup (decl->enumvalues, axl_list_find_string, (char *) value) == NULL) {
2788: /* found an error */
2789: err_msg = axl_strdup_printf ("Found an attribute (%s) with a value not allowed by the enum declaration (%s) for the node <%s>",
2790: key, value, attribute->name);
2791: axl_error_new (-1, err_msg, NULL, error);
2792:
2793: /* free the cursor and the error message */
2794: axl_free (err_msg);
2795: return axl_true;
2796: }
2797: } else {
2798: /* not supported yet */
2799: }
2800:
2801: /* return axl_false to continue with the process */
2802: return axl_false;
2803: }
2804:
2805: axl_bool __axl_dtd_attr_validate_required (axlPointer element, axlPointer data)
2806: {
2807: axlNode * node = data;
2808: axlDtdAttributeDecl * decl = element;
2809:
2810: switch (decl->defaults) {
2811: case ATT_REQUIRED:
2812: /* attribute required */
2813: return !HAS_ATTR (node, decl->name);
2814: case ATT_FIXED:
2815: return !HAS_ATTR_VALUE (node, decl->name, decl->default_value);
2816: default:
2817: break;
2818: } /* end switch */
2819:
2820: /* return axl_false for this because it is not obligatory
2821: * to have the attribute defined. */
2822: return axl_false;
2823: }
2824:
2825: /**
2826: * @internal Functions which validates the attribute declaration for
2827: * the node provided, using attribute declarations found.
2828: *
2829: * @param node The node to check for its attributes.
2830: *
2831: * @param dtd The dtd used to validate the node provided.
2832: *
2833: * @param error A reference to the axlError where the textual
2834: * diagnostic error will be reported.
2835: *
2836: * @return axl_true if the node is validated, axl_false if not.
2837: */
2838: axl_bool axl_dtd_attr_validate (axlNode * node, axlDtd * dtd, axlError ** error, axlHash * id_validation, axlList * idref_validation)
2839: {
2840: axlDtdAttribute * attribute;
2841: axlDtdAttributeDecl * decl;
2842: char * err_msg;
2843: int iterator;
2844: axlError * _error = NULL;
2845:
2846: /* find attribute contraints for the node */
2847: attribute = axl_dtd_get_attr (dtd, axl_node_get_name (node));
2848: if (attribute == NULL)
2849: return axl_true;
2850:
2851: /* we have an especification, run it */
2852:
2853: /* for each attribute found, check against the spec */
2854: axl_node_attr_foreach (node, __axl_dtd_attr_validate_foreach, attribute, &_error);
2855:
2856: /* check the error */
2857: if (! axl_error_was_ok (_error)) {
2858: /* reconfigure error returned */
2859: if (error != NULL)
2860: *error = _error;
2861: return axl_false;
2862: } /* end if */
2863:
2864:
2865: /* now, for each contraint, check that all required nodes
2866: * exists */
2867: decl = axl_list_lookup (attribute->list, __axl_dtd_attr_validate_required, node);
2868: if (decl != NULL) {
2869: if (decl->defaults == ATT_FIXED)
2870: err_msg = axl_strdup_printf ("attribute required '%s' (or its value), due to #FIXED declaration, not found for node <%s>",
2871: decl->name, attribute->name);
2872: else
2873: err_msg = axl_strdup_printf ("attribute required '%s', due to #REQUIRED declaration, not found for node <%s>",
2874: decl->name, attribute->name);
2875: axl_error_new (-1, err_msg, NULL, error);
2876: axl_free (err_msg);
2877: return axl_false;
2878: } /* end if */
2879:
2880: /* check declarations */
2881: if (dtd->haveIdDecl) {
2882:
2883: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD has ID unique attribute declaration..");
2884:
2885: /* check if the node have TOKENIZED_TYPE_ID */
2886: decl = axl_list_lookup (attribute->list, __find_id_decl, NULL);
2887:
2888: /* if we have a tokenized */
2889: if (decl != NULL) {
2890:
2891: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found ID unique attribute declaration %s=\"%s\"..",
2892: decl->name, ATTR_VALUE (node, decl->name));
2893:
2894: /* check if the attribute value for the decl that is
2895: * flagged as ID is already found at the
2896: * id_validation */
2897: if (axl_hash_exists (id_validation, (axlPointer) ATTR_VALUE (node, decl->name))) {
2898: err_msg = axl_strdup_printf ("DTD declared the attribute '%s' as unique (ID) for the node %s, but was found used several times",
2899: decl->name, attribute->name);
2900: axl_error_new (-1, err_msg, NULL, error);
2901: axl_free (err_msg);
2902: return axl_false;
2903: } /* end if */
2904:
2905: /* seems the attribute was not used, nice!, store it */
2906: axl_hash_insert (id_validation, (axlPointer) ATTR_VALUE (node, decl->name), (axlPointer) ATTR_VALUE (node, decl->name));
2907: } /* end if */
2908: } /* end if */
2909:
2910: if (dtd->haveIdRefDecl) {
2911: /* find the id ref declaration */
2912:
2913: iterator = 0;
2914: while (iterator < axl_list_length (attribute->list)) {
2915:
2916: /* get the attribute declaration at the
2917: * particular position */
2918: decl = axl_list_get_nth (attribute->list, iterator);
2919: if (decl->type == TOKENIZED_TYPE_IDREF) {
2920: /* found a reference, but do not check
2921: * it at this place becase the
2922: * reference could be placed at any
2923: * part in the document event after
2924: * the reference pointed is
2925: * defined. store and check later */
2926: if (ATTR_VALUE (node, decl->name)) {
2927: /* store the id ref reference
2928: * if defined */
2929: axl_list_add (idref_validation, (axlPointer) ATTR_VALUE (node, decl->name));
2930: }
2931: } /* end if */
2932:
2933: /* get the next */
2934: iterator++;
2935:
2936: } /* end if */
2937:
2938: } /* end if */
2939:
2940: axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attributes validated for node=<%s>", attribute->name);
2941:
2942: return axl_true;
2943: }
2944:
2945: /**
2946: * @internal Function used by axl_dtd_validate_references to ensure
2947: * that all references found point to a valid reference defined.
2948: */
2949: axl_bool __axl_dtd_reference_check (axlPointer _element, axlPointer data)
2950: {
2951: #if defined(SHOW_DEBUG_LOG)
2952: const char * value = _element;
2953:
2954: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking id ref: %s", value);
2955: #endif
2956:
2957: return ! axl_hash_exists ((axlHash *) data, _element);
2958: }
2959:
2960: /**
2961: * @internal Function that validates all references found (from IDREF
2962: * attribute) to unique references (defined by ID attributes).
2963: *
2964: */
2965: axl_bool axl_dtd_validate_references (axlHash * id_validation, axlList * idref_validation, axlError ** error)
2966: {
2967: char * reference;
2968: char * err_msg;
2969:
2970: /* if no empty at the valiadtion reference list, means not
2971: * reference was done, so there is no room for errors */
2972: if (idref_validation == NULL)
2973: return axl_true;
2974:
2975: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "id_validation reference: 0x%x", id_validation);
2976:
2977: /* find first reference not found */
2978: reference = axl_list_lookup (idref_validation, __axl_dtd_reference_check, id_validation);
2979:
2980: if (reference != NULL) {
2981: /* found a reference not defined, report it to the
2982: * application level */
2983: err_msg = axl_strdup_printf ("Found a reference defined ('%s') which is not found in any ID attribute in the document",
2984: reference);
2985: axl_error_new (-1, err_msg, NULL, error);
2986: axl_free (err_msg);
2987:
2988: return axl_false;
2989: } /* end if */
2990:
2991: /* validation ok */
2992: return axl_true;
2993: }
2994:
2995: /**
2996: * @brief Allows to validate the given XML document (\ref axlDoc)
2997: * against the given document type definition (DTD, \ref axlDtd).
2998: *
2999: * This function allows to validate your XML documents providing the
3000: * document type definition, that was read using \ref axl_dtd_parse or
3001: * \ref axl_dtd_parse_from_file.
3002: *
3003: * Keep in mind that a document could be well-formed and valid. The
3004: * only difference is that valid XML document are those that, meet all
3005: * XML rules, but also are clasified and recognized as XML documents
3006: * with some particular structure, that is represented (or
3007: * constrained) with providing a DTD definition.
3008: *
3009: * @param doc The \ref axlDoc containing the XML document to be
3010: * validated.
3011: *
3012: * @param dtd The \ref axlDtd containing the DTD definition used to
3013: * validate the document.
3014: *
3015: * @param error An optional reference to a \ref axlError object where
3016: * validation errors are reported.
3017: *
3018: * @return axl_true if the document is valid, axl_false if not.
3019: */
3020: axl_bool axl_dtd_validate (axlDoc * doc, axlDtd * dtd,
3021: axlError ** error)
3022: {
3023: axlNode * parent;
3024: axlStack * stack;
3025: axlHash * id_validation = NULL;
3026: axlList * idref_validation = NULL;
3027:
3028: axlDtdElement * element;
3029: axl_bool top_level;
3030: char * err_msg;
3031: axl_bool result;
3032:
3033: /* perform some checkings */
3034: axl_return_val_if_fail (doc, axl_false);
3035: axl_return_val_if_fail (dtd, axl_false);
3036:
3037: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "starting DTD validation");
3038:
3039: /* validate the very first root node */
3040: parent = axl_doc_get_root (doc);
3041: element = axl_dtd_get_root (dtd);
3042: if ((element != NULL) && ! NODE_CMP_NAME (parent, axl_dtd_get_element_name (element))) {
3043:
3044: /* because a DTD document could have several top level
3045: * elements, ensure this is not the case */
3046: element = axl_dtd_get_element (dtd, axl_node_get_name (parent));
3047: if (element == NULL) { /* || ! axl_dtd_element_is_toplevel (dtd, element)) { */
3048: /* root node doesn't match */
3049: err_msg = axl_strdup_printf ("Found that root node doesn't match (%s != %s!",
3050: axl_node_get_name (parent),
3051: axl_dtd_get_element_name (element));
3052: axl_error_new (-1, err_msg, NULL, error);
3053: axl_free (err_msg);
3054: return axl_false;
3055:
3056: } /* end if */
3057: } /* end if */
3058:
3059: /* check if the node has DTD element declaration */
3060: if (element == NULL) {
3061: err_msg = axl_strdup_printf ("There is not DTD element declaration to validate the node <%s>",
3062: axl_node_get_name (parent));
3063: axl_error_new (-1, err_msg, NULL, error);
3064: axl_free (err_msg);
3065: return axl_false;
3066: } /* end if */
3067:
3068: /* check if the dtd contains a Id declaration */
3069: if (dtd->haveIdDecl) {
3070: /* seems the user have declarted ID attributes init the hash */
3071: id_validation = axl_hash_new (axl_hash_string, axl_hash_equal_string);
3072: } /* end if */
3073:
3074: /* check if the dtd contains Id ref declarations */
3075: if (dtd->haveIdRefDecl) {
3076: /* create a list that could contain all references done */
3077: idref_validation = axl_list_new (axl_list_always_return_1, NULL);
3078: } /* end if */
3079:
3080: /* check empty content spec */
3081: if (axl_dtd_get_element_type (element) == ELEMENT_TYPE_EMPTY) {
3082: /* check if the document provided have only one node */
3083: result = axl_node_is_empty (parent) && !axl_node_have_childs (parent) && axl_dtd_attr_validate (parent, dtd, error, id_validation, idref_validation);
3084:
3085: /* check references */
3086: if (result)
3087: result = axl_dtd_validate_references (id_validation, idref_validation, error);
3088:
3089: /* free and return */
3090: axl_hash_free (id_validation);
3091:
3092: /* free the list */
3093: axl_list_free (idref_validation);
3094: return result;
3095: } /* end if */
3096:
3097: /* queue initial nodes to validate */
3098: stack = axl_stack_new (NULL);
3099:
3100:
3101: /* set that the only top level node is the first one */
3102: top_level = axl_true;
3103:
3104: do {
3105: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "doing a DTD iteration: <%s>...",
3106: axl_node_get_name (parent));
3107:
3108: /* validate attributes */
3109: if (! axl_dtd_attr_validate (parent, dtd, error, id_validation, idref_validation)) {
3110: /* free the stack */
3111: axl_stack_free (stack);
3112:
3113: /* free id_validation */
3114: axl_hash_free (id_validation);
3115:
3116: /* free the list */
3117: axl_list_free (idref_validation);
3118: return axl_false;
3119: }
3120:
3121: /* reach this position, the <parent> reference contains
3122: * a reference to the parent node, which will be used
3123: * to validate current child content against current
3124: * configuration for dtd element constraining it.
3125: *
3126: * equally, the <element> reference contains a dtd
3127: * reference to the already checked DTD element which
3128: * configure this parent node. */
3129: switch (axl_dtd_get_element_type (element)) {
3130: case ELEMENT_TYPE_PCDATA:
3131: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find PCDATA dtd element=%s: parent=<%s>, ",
3132: axl_dtd_get_element_name (element),
3133: axl_node_get_name (parent));
3134:
3135: /* ok, a leaf node was found, know it is
3136: * required to check that the node doesn't
3137: * have more childs and only have content,
3138: * that is, it is not empty */
3139: if (!__axl_dtd_validate_element_type_pcdata (element, parent, stack, error)) {
3140: /* free id_validation */
3141: axl_hash_free (id_validation);
3142:
3143: /* free the stack */
3144: axl_stack_free (stack);
3145:
3146: /* free the list */
3147: axl_list_free (idref_validation);
3148: return axl_false;
3149: }
3150: break;
3151: case ELEMENT_TYPE_CHILDREN:
3152:
3153: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find CHILDREN dtd element");
3154: /* ok, a parent node that have childs */
3155: if (!__axl_dtd_validate_element_type_children (element, parent, top_level, error)) {
3156: /* free id_validation */
3157: axl_hash_free (id_validation);
3158:
3159: /* free the stack */
3160: axl_stack_free (stack);
3161:
3162: /* free the list */
3163: axl_list_free (idref_validation);
3164:
3165: return axl_false;
3166: }
3167: break;
3168: case ELEMENT_TYPE_EMPTY:
3169: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find EMPTY dtd element");
3170: /* the element especification is empty, the
3171: * node being validated must also be the
3172: * same */
3173: if (!__axl_dtd_validate_element_type_empty (element, parent, stack, error)) {
3174: /* free id_validation */
3175: axl_hash_free (id_validation);
3176:
3177: /* free the stack */
3178: axl_stack_free (stack);
3179:
3180: /* free the list */
3181: axl_list_free (idref_validation);
3182:
3183: return axl_false;
3184: }
3185: break;
3186: case ELEMENT_TYPE_ANY:
3187: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find ANY dtd element");
3188: /* the anything is allowed cased from this
3189: * parent node. */
3190: goto continue_with_validation;
3191: case ELEMENT_TYPE_MIXED:
3192: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find MIXED dtd element");
3193:
3194: /* the mixed case, where nodes and PC data
3195: * could be mixed */
3196: break;
3197: default:
3198: /* do not do any thing on this case */
3199: break;
3200: }
3201:
3202: /* queue more childs, as future parents to be
3203: * validated on the provided queue, only in the case
3204: * the parent node have childs */
3205: if (axl_node_have_childs (parent)) {
3206:
3207: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> have childs, adding its childs (stack size: %d)",
3208: axl_node_get_name (parent),
3209: axl_stack_size (stack));
3210:
3211: /* queue childs to be processed */
3212: __axl_dtd_queue_childs (stack, parent);
3213:
3214: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> childs: %d, (stack size: %d)",
3215: axl_node_get_name (parent), axl_node_get_child_num (parent),
3216: axl_stack_size (stack));
3217: }
3218:
3219: /* set the parent reference to NULL */
3220: parent = NULL;
3221:
3222: /* update the reference to the new parent node, only
3223: * if there are new parents on the stack */
3224: continue_with_validation:
3225: if (! axl_stack_is_empty (stack)) {
3226:
3227:
3228: /* get a new reference */
3229: parent = axl_stack_pop (stack);
3230:
3231: /* get a reference to the DTD element to used */
3232: element = axl_dtd_get_element (dtd, axl_node_get_name (parent));
3233: if (element == NULL) {
3234: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the node <%s> doesn't have DTD especification",
3235: axl_node_get_name (parent));
3236: /* prepare the error message */
3237: err_msg = axl_strdup_printf ("Found a node <%s> that doesn't have a DTD element espefication to validate it, DTD validation failed",
3238: axl_node_get_name (parent));
3239: axl_error_new (-1, err_msg, NULL, error);
3240: axl_free (err_msg);
3241:
3242: /* free id_validation */
3243: axl_hash_free (id_validation);
3244:
3245: /* free the list */
3246: axl_list_free (idref_validation);
3247:
3248: /* free the stack */
3249: axl_stack_free (stack);
3250: return axl_false;
3251: } /* end if */
3252: } /* end if */
3253:
3254: /* set the top level status */
3255: top_level = axl_false;
3256:
3257: /* until the stack is empty */
3258: }while (parent != NULL);
3259:
3260: /* check references */
3261: result = axl_dtd_validate_references (id_validation, idref_validation, error);
3262:
3263: /* deallocate stack used */
3264: axl_stack_free (stack);
3265:
3266: /* free id_validation */
3267: axl_hash_free (id_validation);
3268:
3269: /* free the list */
3270: axl_list_free (idref_validation);
3271:
3272: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD validation, %s", result ? "ok" : "failed");
3273:
3274: /* the document is valid */
3275: return result;
3276: }
3277:
3278: /**
3279: * @brief Allows to check if the provided two references represents
3280: * DTD documents with the same rules.
3281: *
3282: * @param dtd First reference to compare.
3283: * @param dtd2 Second reference to compare.
3284: *
3285: * @return axl_true if both references represent the same document. If
3286: * some of the references received are NULL the function returns
3287: * axl_false.
3288: *
3289: * NOTE: The function does not have the ability to perform a smart
3290: * equal operation like detecting DTD that are semantically
3291: * equivalent. It only checks internal structure.
3292: */
3293: axl_bool axl_dtd_are_equal (axlDtd * dtd,
3294: axlDtd * dtd2)
3295: {
3296: int iterator;
3297: int iterator2;
3298: int iterator3;
3299: axlDtdEntity * entity, * entity2;
3300: axlDtdElement * element, * element2;
3301: axlDtdElementListNode * node, * node2;
3302: axlDtdAttribute * attribute, * attribute2;
3303: axlDtdAttributeDecl * attr_decl, * attr_decl2;
3304:
3305: /* check references received */
3306: if (dtd == NULL)
3307: return axl_false;
3308: if (dtd2 == NULL)
3309: return axl_false;
3310:
3311: /* check each rule inside both documents */
3312: if (axl_list_length (dtd->entities) != axl_list_length (dtd2->entities))
3313: return axl_false;
3314: if (axl_list_length (dtd->elements) != axl_list_length (dtd2->elements))
3315: return axl_false;
3316: if (axl_list_length (dtd->attributes) != axl_list_length (dtd2->attributes))
3317: return axl_false;
3318: if (dtd->haveIdRefDecl != dtd2->haveIdRefDecl)
3319: return axl_false;
3320: if (dtd->haveIdDecl != dtd2->haveIdDecl)
3321: return axl_false;
3322:
3323: /* now check inner elements (ENTITIES) */
3324: iterator = 0;
3325: while (iterator < axl_list_length (dtd->entities)) {
3326: /* get referneces */
3327: entity = axl_list_get_nth (dtd->entities, iterator);
3328: entity2 = axl_list_get_nth (dtd2->entities, iterator);
3329:
3330: /* check types */
3331: if (entity->type != entity2->type)
3332: return axl_false;
3333:
3334: /* check names */
3335: if (! axl_cmp (entity->name, entity2->name))
3336: return axl_false;
3337:
3338: /* check content */
3339: if (! axl_cmp (entity->content, entity2->content))
3340: return axl_false;
3341:
3342: /* check external data */
3343: if (entity->data == NULL && entity2->data != NULL)
3344: return axl_false;
3345: if (entity->data != NULL && entity2->data == NULL)
3346: return axl_false;
3347: if (entity->data != NULL && entity2->data != NULL) {
3348: if (! axl_cmp (entity->data->system_literal, entity2->data->system_literal))
3349: return axl_false;
3350: if (! axl_cmp (entity->data->public_literal, entity2->data->public_literal))
3351: return axl_false;
3352: if (! axl_cmp (entity->data->ndata, entity2->data->ndata))
3353: return axl_false;
3354: } /* end if */
3355:
3356: /* next iterator */
3357: iterator++;
3358: } /* end while */
3359:
3360: /* now check inner elements (ELEMENTS) */
3361: iterator = 0;
3362: while (iterator < axl_list_length (dtd->elements)) {
3363: /* get referneces */
3364: element = axl_list_get_nth (dtd->elements, iterator);
3365: element2 = axl_list_get_nth (dtd2->elements, iterator);
3366:
3367: /* check types */
3368: if (element->type != element2->type)
3369: return axl_false;
3370:
3371: /* minimum match */
3372: if (element->minimum_match != element2->minimum_match)
3373: return axl_false;
3374:
3375: /* check names */
3376: if (! axl_cmp (element->name, element2->name))
3377: return axl_false;
3378:
3379: /* check element list */
3380: if (element->list == NULL && element2->list != NULL)
3381: return axl_false;
3382: if (element->list != NULL && element2->list == NULL)
3383: return axl_false;
3384: if (element->list != NULL && element2->list != NULL) {
3385:
3386: /* check internal values */
3387: if (element->list->type != element2->list->type)
3388: return axl_false;
3389: if (element->list->times != element2->list->times)
3390: return axl_false;
3391:
3392: iterator2 = 0;
3393: while (iterator2 < axl_list_length (element->list->itemList)) {
3394:
3395: /* get references */
3396: node = axl_list_get_nth (element->list->itemList, iterator2);
3397: node2 = axl_list_get_nth (element2->list->itemList, iterator2);
3398:
3399: if (node->type != node->type)
3400: return axl_false;
3401: if (node->times != node2->times)
3402: return axl_false;
3403:
3404: /* next value */
3405: iterator2++;
3406:
3407: } /* end while */
3408:
3409: } /* end if */
3410:
3411: /* next iterator */
3412: iterator++;
3413: } /* end while */
3414:
3415: /* now check inner elements (ATTRIBUTES) */
3416: iterator = 0;
3417: while (iterator < axl_list_length (dtd->attributes)) {
3418: /* get referneces */
3419: attribute = axl_list_get_nth (dtd->attributes, iterator);
3420: attribute2 = axl_list_get_nth (dtd2->attributes, iterator);
3421:
3422: /* check names */
3423: if (! axl_cmp (attribute->name, attribute2->name))
3424: return axl_false;
3425:
3426: /* check values */
3427: if (attribute->list == NULL && attribute2->list != NULL)
3428: return axl_false;
3429: if (attribute->list != NULL && attribute2->list == NULL)
3430: return axl_false;
3431: if (attribute->list != NULL && attribute2->list != NULL) {
3432:
3433: /* check list length */
3434: if (axl_list_length (attribute->list) != axl_list_length (attribute2->list))
3435: return axl_false;
3436:
3437: /* check internal values */
3438: iterator2 = 0;
3439: while (iterator2 < axl_list_length (attribute->list)) {
3440:
3441: /* get references */
3442: attr_decl = axl_list_get_nth (attribute->list, iterator2);
3443: attr_decl2 = axl_list_get_nth (attribute2->list, iterator2);
3444:
3445: if (attr_decl->type != attr_decl2->type)
3446: return axl_false;
3447: if (attr_decl->defaults != attr_decl2->defaults)
3448: return axl_false;
3449: if (! axl_cmp (attr_decl->name, attr_decl2->name))
3450: return axl_false;
3451:
3452: if (attr_decl->enumvalues == NULL && attr_decl2->enumvalues != NULL)
3453: return axl_false;
3454: if (attr_decl->enumvalues != NULL && attr_decl2->enumvalues == NULL)
3455: return axl_false;
3456: if (attr_decl->enumvalues != NULL && attr_decl2->enumvalues != NULL) {
3457: if (axl_list_length (attr_decl->enumvalues) != axl_list_length (attr_decl2->enumvalues))
3458: return axl_false;
3459: iterator3 = 0;
3460: while (iterator3 < axl_list_length (attr_decl->enumvalues)) {
3461: /* check values */
3462: if (! axl_cmp (axl_list_get_nth (attr_decl->enumvalues, iterator3),
3463: axl_list_get_nth (attr_decl2->enumvalues, iterator3)))
3464: return axl_false;
3465:
3466: /* next value */
3467: iterator3++;
3468: } /* end while */
3469: } /* end if */
3470:
3471: /* next value */
3472: iterator2++;
3473:
3474: } /* end while */
3475:
3476: } /* end if */
3477:
3478: /* next iterator */
3479: iterator++;
3480: } /* end while */
3481:
3482: return axl_true;
3483:
3484: }
3485:
3486: /**
3487: * @brief Allows to get the root node for the provided DTD.
3488: *
3489: * Every DTD have a root node defined, which is the root node accepted
3490: * for the set of XML document considered to be valid under the
3491: * definition of the DTD provided.
3492: *
3493: * The value returned is the name of the root node that must have the
3494: * XML document being validated.
3495: *
3496: * @param dtd The \ref axlDtd where the root node name will be
3497: * returned.
3498: *
3499: * @return A reference to the internal representation of the root node
3500: * Value must not be deallocated.
3501: */
3502: axlDtdElement * axl_dtd_get_root (axlDtd * dtd)
3503: {
3504: axl_return_val_if_fail (dtd, NULL);
3505:
3506: /* return current status for the root node */
3507: if (dtd->root == NULL) {
3508: __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "dtd root element not defined");
3509: return NULL;
3510: }
3511: return dtd->root;
3512: }
3513:
3514: /**
3515: * @internal function used by \ref axl_dtd_get_element to perform node
3516: * lookups.
3517: */
3518: axl_bool __find_dtd_element (axlPointer _element, axlPointer data)
3519: {
3520: axlDtdElement * element = _element;
3521: char * name = data;
3522:
3523: /* check the name */
3524: if (axl_cmp (element->name, name))
3525: return axl_true;
3526:
3527: /* it is not the element */
3528: return axl_false;
3529: }
3530:
3531: /**
3532: * @brief Allows to get the DTD element (\ref axlDtdElement), inside
3533: * the provided DTD (\ref axlDtd), that represent the spefication for
3534: * the node called by the provided name.
3535: *
3536: * @param dtd The DTD (\ref axlDtd) where the lookup will be
3537: * performed.
3538: *
3539: * @param name The element name to lookup.
3540: *
3541: * @return A reference to the \ref axlDtdElement searched or NULL if
3542: * fails. The function also returns NULL if values received are NULL.
3543: */
3544: axlDtdElement * axl_dtd_get_element (axlDtd * dtd, const char * name)
3545: {
3546:
3547: axl_return_val_if_fail (dtd, NULL);
3548: axl_return_val_if_fail (name, NULL);
3549:
3550: /* perform the lookup */
3551: return axl_list_lookup (dtd->elements, __find_dtd_element, (axlPointer) name);
3552: }
3553:
3554: /**
3555: * @internal function used by \ref axl_dtd_get_attr to perform node
3556: * lookups.
3557: */
3558: axl_bool __find_dtd_attr (axlPointer _element, axlPointer data)
3559: {
3560: axlDtdAttribute * attr = _element;
3561: char * name = data;
3562:
3563: /* check the name */
3564: if (axl_cmp (attr->name, name))
3565: return axl_true;
3566:
3567: /* it is not the element */
3568: return axl_false;
3569: }
3570:
3571: /**
3572: * @brief Allows to get the set of attribute declerations for a
3573: * particular node.
3574: *
3575: * The \ref axlDtdAttribute declaration contains all constraints
3576: * configured for attributes found for the particular xml node
3577: * (identified by <b>name</b>).
3578: *
3579: * @param dtd A reference to the DTD document.
3580: *
3581: * @param nodeName The xml node that is requested to return all attribute
3582: * declarations.
3583: *
3584: * @return A reference to the \ref axlDtdAttribute or NULL if it
3585: * fails.
3586: */
3587: axlDtdAttribute * axl_dtd_get_attr (axlDtd * dtd,
3588: const char * nodeName)
3589: {
3590: axl_return_val_if_fail (dtd, NULL);
3591: axl_return_val_if_fail (nodeName, NULL);
3592:
3593: /* perform the lookup */
3594: return axl_list_lookup (dtd->attributes, __find_dtd_attr, (axlPointer) nodeName);
3595: }
3596:
3597: /**
3598: * @brief Allows to get the number of constraints that have been
3599: * configured for the particular node.
3600: *
3601: * @param dtd The reference to the DTD document.
3602: *
3603: * @param nodeName The name of the node that is being asked for its
3604: * constraints.
3605: *
3606: * @return 0 or the number of contraints. The function return -1 if
3607: * any of the parameter received is null.
3608: */
3609: int axl_dtd_get_attr_contraints (axlDtd * dtd,
3610: const char * nodeName)
3611: {
3612: axlDtdAttribute * attr;
3613:
3614: axl_return_val_if_fail (dtd, -1);
3615: axl_return_val_if_fail (nodeName, -1);
3616:
3617: /* get the attribute specification for the node */
3618: attr = axl_dtd_get_attr (dtd, nodeName);
3619:
3620: /* return the number of items */
3621: return axl_list_length (attr->list);
3622: }
3623:
3624: /**
3625: * @brief Returns the name of the provided \ref axlDtdElement.
3626: *
3627: * @param element A reference to a \ref axlDtdElement where the name
3628: * will be returned.
3629: *
3630: * @return A reference to the internal DTD element name. Returned
3631: * value mustn't be deallocated.
3632: */
3633: char * axl_dtd_get_element_name (axlDtdElement * element)
3634: {
3635: axl_return_val_if_fail (element, NULL);
3636:
3637: return element->name;
3638: }
3639:
3640: /**
3641: * @brief Returns current element type for the provided \ref axlDtdElement.
3642: *
3643: * @param element The axlDtdElement where its type will be returned.
3644: *
3645: * @return Current element type for the provided node.
3646: */
3647: AxlDtdElementType axl_dtd_get_element_type (axlDtdElement * element)
3648: {
3649: axl_return_val_if_fail (element, ELEMENT_TYPE_UNKNOWN);
3650:
3651: return element->type;
3652: }
3653:
3654: /**
3655: * @brief Returns current DTD content specification, represented by the Item list.
3656: *
3657: * @param element The DTD element (\ref axlDtdElement) which is being
3658: * requested to return its \ref axlDtdElementList.
3659: *
3660: * @return The \ref axlDtdElementList reference. The value returned
3661: * must not be deallocated. The function returns NULL if the reference received is NULL.
3662: */
3663: axlDtdElementList * axl_dtd_get_item_list (axlDtdElement * element)
3664: {
3665: axl_return_val_if_fail (element, NULL);
3666:
3667: return element->list;
3668: }
3669:
3670: /**
3671: * @brief Allows to check if the provided DTD ELEMENT representation
3672: * is a top level definition.
3673: *
3674: * @param dtd The DTD document where the operation will be performed.
3675: * @param element The \ref axlDtdElement to check.
3676: *
3677: * @return \ref axl_true if the dtd element is a top level element or
3678: * \ref axl_false if not. The function returns \ref axl_false if the
3679: * provided reference is NULL.
3680: */
3681: axl_bool axl_dtd_element_is_toplevel (axlDtd * dtd, axlDtdElement * element)
3682: {
3683: /* support several top level definitions */
3684: int iterator;
3685: axlDtdElement * dtd_element_aux;
3686:
3687: axl_return_val_if_fail (dtd, axl_false);
3688: axl_return_val_if_fail (element, axl_false);
3689:
3690: /* check which is the top */
3691: iterator = 0;
3692: while (iterator < axl_list_length (dtd->elements)) {
3693:
3694: /* get the next reference */
3695: dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
3696:
3697: /* check which is the top */
3698: if (__axl_dtd_get_is_parent (dtd_element_aux, element)) {
3699: /* the element provided have a parent */
3700: return axl_false;
3701: }
3702:
3703: /* update inner loop iterator */
3704: iterator ++;
3705: } /* while end */
3706:
3707: /* return that the provided node doesn't have a parent node */
3708: return axl_true;
3709: }
3710:
3711: /**
3712: * @brief Returns the number of item nodes (\ref
3713: * axlDtdElementListNode) inside the item list received (\ref axlDtdElementList).
3714: *
3715: * @param itemList The \ref axlDtdElementList where the count
3716: * operation is being requested.
3717: *
3718: * @return The number of item list the provided \ref axlDtdElementList
3719: * reference has. The function return -1 if the provided reference is
3720: * NULL.
3721: */
3722: int axl_dtd_item_list_count (axlDtdElementList * itemList)
3723: {
3724: axl_return_val_if_fail (itemList, -1);
3725:
3726: if (itemList->itemList == NULL)
3727: return 0;
3728:
3729: return axl_list_length (itemList->itemList);
3730: }
3731:
3732: /**
3733: * @brief Allows to get current configuration for the provided item
3734: * list, which is the content specification for a DTD element.
3735: *
3736: * @param itemList The item list where the operation will be
3737: * performed.
3738: *
3739: * @return Current configuration (\ref SEQUENCE or a \ref CHOICE).
3740: */
3741: AxlDtdNestedType axl_dtd_item_list_type (axlDtdElementList * itemList)
3742: {
3743: axl_return_val_if_fail (itemList, -1);
3744:
3745: return itemList->type;
3746: }
3747:
3748: /**
3749: * @brief Allows to get current configuration for DTD content spec
3750: * repetition.
3751: *
3752: * @param itemList The content spec where the query will be performed.
3753: *
3754: * @return Current configuration for times to be repeated DTD element
3755: * content specification.
3756: */
3757: AxlDtdTimes axl_dtd_item_list_repeat (axlDtdElementList * itemList)
3758: {
3759: axl_return_val_if_fail (itemList, DTD_TIMES_UNKNOWN);
3760:
3761: /* returns current times configuration */
3762: return itemList->times;
3763: }
3764:
3765: /**
3766: * @brief Allows to get the provided item node reference (\ref
3767: * axlDtdElementListNode) from the provided item list (\ref
3768: * axlDtdElementList).
3769: *
3770: * Provided position ranges from 0 up to \ref axl_dtd_item_list_count.
3771: *
3772: * @param itemList The itemList where the operation will be performed.
3773: * @param position The position where the item node will be looked up.
3774: *
3775: * @return A reference to the \ref axlDtdElementListNode, or NULL if
3776: * there is no item node at the selected index. The function return
3777: * NULL if the provided position is a non positive value or it is
3778: * greater than the current item list count (\ref
3779: * axl_dtd_item_list_count) or the provided item list reference is
3780: * NULL.
3781: */
3782: axlDtdElementListNode * axl_dtd_item_list_get_node (axlDtdElementList * itemList,
3783: int position)
3784: {
3785: axl_return_val_if_fail (itemList, NULL);
3786: axl_return_val_if_fail (position >= 0, NULL);
3787: axl_return_val_if_fail (position < axl_dtd_item_list_count (itemList), NULL);
3788:
3789: return axl_list_get_nth (itemList->itemList, position);
3790: }
3791:
3792: /**
3793: * @brief Allows to get current node type for the provided DTD element
3794: * type content particule or item node (\ref axlDtdElementListNode).
3795: *
3796: * @param node The node where the type is being requested.
3797: *
3798: * @return It returns if the item node contains a final leaf node,
3799: * making a reference to an explicit node naming that is allowed to be
3800: * used in the context where is found the provided \ref
3801: * axlDtdElementListNode or a \ref axlDtdElementList containing more
3802: * nodes or lists.
3803: */
3804: NodeType axl_dtd_item_node_get_type (axlDtdElementListNode * node)
3805: {
3806: axl_return_val_if_fail (node, AXL_ELEMENT_NOT_DEFINED);
3807: return node->type;
3808: }
3809:
3810: /**
3811: * @brief Returns the item list inside the provided node.
3812: *
3813: * The node is supported to contain an item list reference or NULL
3814: * will be returned. Check \ref axl_dtd_item_node_get_type.
3815: *
3816: * @param node The node where the operation will be performed.
3817: *
3818: * @return The item list inside the node or NULL if fails.
3819: */
3820: axlDtdElementList * axl_dtd_item_node_get_list (axlDtdElementListNode * node)
3821: {
3822: axl_return_val_if_fail (node, NULL);
3823: axl_return_val_if_fail (node->type == AXL_ELEMENT_LIST, NULL);
3824:
3825: return node->data;
3826: }
3827:
3828: /**
3829: * @brief Allows to get the dtd item list value, which represents the
3830: * node name that is being constrained/represented.
3831: *
3832: * @param node The item node where the value is being requested.
3833: *
3834: * @return The value inside the item node, supposing it contains an
3835: * leaf item node or NULL if fails. The value returned must not be
3836: * deallocated.
3837: */
3838: char * axl_dtd_item_node_get_value (axlDtdElementListNode * node)
3839: {
3840: axl_return_val_if_fail (node, NULL);
3841: if (node->type != AXL_ELEMENT_NODE)
3842: return "requested-value-on-a-list";
3843:
3844: return node->data;
3845: }
3846:
3847: /**
3848: * @brief Allows to get current configuration for the provided content
3849: * particule for the times to be repeated.
3850: *
3851: * @param node The content particule where the query will be
3852: * performed.
3853: *
3854: * @return Return current repetition configuration.
3855: */
3856: AxlDtdTimes axl_dtd_item_node_get_repeat (axlDtdElementListNode * node)
3857: {
3858: axlDtdElementList * list;
3859:
3860: axl_return_val_if_fail (node, DTD_TIMES_UNKNOWN);
3861:
3862:
3863: if (node->type == AXL_ELEMENT_NODE) {
3864: /* return value requested */
3865: return node->times;
3866: }
3867:
3868: if (node->type == AXL_ELEMENT_LIST) {
3869: /* return the requested value for an item list */
3870: list = node->data;
3871: return list->times;
3872: }
3873:
3874: /* return that we don't know man */
3875: return DTD_TIMES_UNKNOWN;
3876: }
3877:
3878:
3879: /**
3880: * @internal
3881: *
3882: * Internal function which allows to lookup the DTD entity reference
3883: * provided the name and the type.
3884: */
3885: axlDtdEntity * __axl_dtd_entity_lookup (axlDtd * dtd,
3886: const char * name,
3887: axlDtdEntityType type)
3888: {
3889: axlDtdEntity * entity;
3890: int iterator;
3891: int length;
3892:
3893: /* check values received */
3894: axl_return_val_if_fail (dtd, NULL);
3895: axl_return_val_if_fail (name, NULL);
3896:
3897: /* lookup for the item */
3898: iterator = 0;
3899: length = axl_list_length (dtd->entities);
3900: while (iterator < length) {
3901:
3902: /* get the entity at the provided position */
3903: entity = axl_list_get_nth (dtd->entities, iterator);
3904:
3905: /* check the type and the name */
3906: if ((entity->type == type) && axl_cmp (entity->name, name))
3907: return entity;
3908:
3909: /* update iterator */
3910: iterator++;
3911: } /* end while */
3912:
3913: return NULL;
3914: }
3915:
3916: /**
3917: * @brief Allows to check if the provided entity name, with the
3918: * provided type is defined on the given DTD object.
3919: *
3920: * @param dtd The \ref axlDtd instance where the entity lookup will be
3921: * performed.
3922: *
3923: * @param name The entity name to lookup.
3924: *
3925: * @param type The entity type to lookup.
3926: *
3927: * @return axl_true if an entity is found named as provided with the type
3928: * provided. Othewise, axl_false is returned.
3929: */
3930: axl_bool axl_dtd_entity_exists (axlDtd * dtd,
3931: const char * name,
3932: axlDtdEntityType type)
3933: {
3934: /* return if the entity exists */
3935: return (__axl_dtd_entity_lookup (dtd, name, type) != NULL);
3936: }
3937:
3938: /**
3939: * @brief Allows to get the content configured inside the entity that
3940: * is identified by the provided name and the provided type.
3941: *
3942: * @param dtd The DTD where the lookup will be performed.
3943: *
3944: * @param name The entity name to lookup for its content.
3945: *
3946: * @param type The entity type to match.
3947: *
3948: * @return An internal reference to the content associated to the
3949: * entity found or NULL. In case the content is defined (as return
3950: * value) it must not be deallocated.
3951: */
3952: char * axl_dtd_entity_value (axlDtd * dtd,
3953: const char * name,
3954: axlDtdEntityType type)
3955: {
3956: axlDtdEntity * entity;
3957:
3958: /* get the entity reference */
3959: entity = __axl_dtd_entity_lookup (dtd, name, type);
3960:
3961: /* check the entity reference */
3962: axl_return_val_if_fail (entity, NULL);
3963:
3964: /* return the content */
3965: return entity->content;
3966: }
3967:
3968: /**
3969: * @brief Allows to destroy the provided \ref axlDtd document.
3970: *
3971: * @param dtd The \ref axlDtd document to destroy.
3972: */
3973: void axl_dtd_free (axlDtd * dtd)
3974: {
3975: if (dtd == NULL) {
3976: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "received a null DTD reference, doing nothing");
3977: return;
3978: }
3979:
3980: __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "releasing the DTD reference");
3981: /* free dtd elements */
3982: if (dtd->elements)
3983: axl_list_free (dtd->elements);
3984:
3985: /* free entities */
3986: if (dtd->entities)
3987: axl_list_free (dtd->entities);
3988:
3989: /* free attributes */
3990: if (dtd->attributes)
3991: axl_list_free (dtd->attributes);
3992:
3993: /* free the node itself */
3994: axl_free (dtd);
3995:
3996: return;
3997: }
3998:
3999:
4000: /**
4001: * @internal
4002: *
4003: * @brief Allows to release the memory hold by the given
4004: * axlDtdElement.
4005: *
4006: * @param element The axlDtdElement to release.
4007: */
4008: void axl_dtd_element_free (axlDtdElement * element)
4009: {
4010: if (element == NULL)
4011: return;
4012:
4013: /* free element name */
4014: if (element->name != NULL)
4015: axl_free (element->name);
4016:
4017: /* free element list definitions */
4018: axl_dtd_item_list_free (element->list);
4019:
4020: /* free element itself */
4021: axl_free (element);
4022:
4023: return;
4024: }
4025:
4026: /**
4027: * @internal
4028: *
4029: * @brief Deallocates memory used by the \ref axlDtdElementList
4030: * reference.
4031: *
4032: * @param list The reference to deallocate.
4033: */
4034: void axl_dtd_item_list_free (axlDtdElementList * list)
4035: {
4036: if (list == NULL)
4037: return;
4038:
4039: /* check and deallocate the list provided */
4040: if (list->itemList != NULL)
4041: axl_list_free (list->itemList);
4042:
4043: /* deallocates the node itself */
4044: axl_free (list);
4045: return;
4046: }
4047:
4048: /* @} */
4049:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>