Add missing files
[gmpc-magnatune.git] / src / axl / axl_dtd.c
blob534d168097b5017572c7b03e15ebc3505acb807a
1 /**
2 * LibAxl: Another XML library
3 * Copyright (C) 2006 Advanced Software Production Line, S.L.
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.
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.
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
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.
27 * For commercial support on build XML enabled solutions contact us:
29 * Postal address:
30 * Advanced Software Production Line, S.L.
31 * C/ Dr. Michavila NÂș 14
32 * Coslada 28820 Madrid
33 * Spain
35 * Email address:
36 * info@aspl.es - http://fact.aspl.es
38 #include <axl_decl.h>
39 #include <axl.h>
41 #define LOG_DOMAIN "axl-dtd"
43 struct _axlDtdElementListNode {
44 NodeType type;
45 AxlDtdTimes times;
46 axlPointer data;
49 struct _axlDtdElementList {
50 /**
51 * @brief Allows to configure how is given top level
52 * configuration for nodes to be defined inside the xml
53 * document being configured. As defined in the XML 1.0
54 * Recomendation, available top level choices are: choice or
55 * sequence.
57 * They allow to configure allowed nodes to be selected as
58 * childs, from a set of node names, called choice or to
59 * configure which are the set of nodes to be used, in a
60 * particular order, called sequence.
62 * This variable allows to configure which is the top level
63 * section configuration: either a choice or a sequence.
65 * Keep in mind that, having only one element inside the
66 * itemList, there is no difference between the sequence and
67 * the choice.
69 AxlDtdNestedType type;
71 /**
72 * @brief Allows to configure how many times is repeated a
73 * selection provided (by this element).
75 AxlDtdTimes times;
77 /**
78 * @brief Item list, which contains more axlDtdElementList
79 * nodes, configuring elements allowed.
81 axlList * itemList;
84 struct _axlDtdElement {
85 /**
86 * @brief The document type element declaration name. This is
87 * the name of the xml node being constrained.
89 char * name;
91 /**
92 * @brief This is the type of the xml node being constrained.
94 AxlDtdElementType type;
95 /**
96 * @brief List of available items.
98 * This variable holds current top level list selection. See
99 * axlDtdElementList.type variable.
101 axlDtdElementList * list;
103 /**
104 * @brief Minimum item list count to be matched while using
105 * this DTD element rule.
107 int minimum_match;
110 struct _axlDtdAttributeDecl {
111 /**
112 * @brief Attribute name. This is the attribute value defined
113 * for the node.
115 char * name;
117 /**
118 * @brief This is the attribute declaration type. It shows if
120 AxlDtdAttributeType type;
122 /**
123 * @brief Allows to model whoe is
125 AxlDtdAttributeDefaults defaults;
127 /**
128 * @brief This is a default value for the <!ATTLIST
129 * declaration received, in the case a FIXED value is required
130 * or a default value is declarted.
132 char * default_value;
134 /**
135 * @brief Internal declaration for enum values defined for
136 * this rule. This list is only initialized in the case enum
137 * values are defined.
139 axlList * enumvalues;
143 struct _axlDtdAttribute {
144 /**
145 * @brief The document attribute list declaration name. This
146 * is the name of the node that will receive the constrain
147 * defined.
149 char * name;
151 /**
152 * @brief This is the list of constrains defined for the
153 * node. It as list of \ref axlDtdAttributeDecl which defines
154 * the attribute that is declarted, if it is required, and the
155 * type of its content.
157 axlList * list;
160 struct _axlDtdEntityExternalData {
161 /**
162 * @brief Contains the system literal reference. This is a URI
163 * reference to the resource pointed by the \ref axlDtdEntity
164 * definition.
166 char * system_literal;
167 /**
168 * @brief Contains the public literal information associated
169 * to the entity definition.
171 char * public_literal;
172 /**
173 * @brief Contains the NDATA information (a notation name
174 * reference).
176 char * ndata;
179 struct _axlDtdEntity {
180 /**
181 * @brief Contains the entity name.
183 char * name;
185 /**
186 * @brief Contains the entity type.
188 axlDtdEntityType type;
190 /**
191 * @brief Content of the entity definition ([9] EntityValue).
193 char * content;
195 /**
196 * @brief An entity definition can have a reference to a
197 * external resource. The following pointer contains
198 * information for the external resource pointed.
200 axlDtdEntityExternalData * data;
203 struct _axlDtd {
204 /**
205 * @brief Holds all entity definitions inside the DTD
206 * declaration (<!ENTITY..>).
208 axlList * entities;
210 /**
211 * @brief All elements inside the DTD declaration
212 * (<!ELEMENT..> ).
214 axlList * elements;
216 /**
217 * @brief All attribute type declerations inside the DTD
218 * (<!ATTLIST..>)
220 axlList * attributes;
222 /**
223 * @brief The element root, for the given DTD declaration.
225 axlDtdElement * root;
227 /**
228 * @brief Internal flag that allows to notify that the DTD
229 * contains ID attribute declaration, making the DTD this
230 * references.
232 bool haveIdDecl;
234 /**
235 * @brief Flag that the dtd declaration have attributes which
236 * are flaged as IDREF.
238 bool haveIdRefDecl;
242 * \defgroup axl_dtd_module Axl DTD: Document type declaration interface (functions, validation, and DTD parsing)
246 /**
247 * \addtogroup axl_dtd_module
248 * @{
252 /**
253 * @internal
255 * Allows to create a new dtd element list item, which represents a
256 * content particule inside an item list or a item list. This allows
257 * the recursion defined on the XML 1.0 standard.
259 * The function receives the node name and a reference list. According
260 * to the values the function creates a node which contains a leaf
261 * value or a node which contains a reference to the a new list which
262 * is nested.
264 axlDtdElementListNode * __create_axl_dtd_element_list (char * node_name,
265 axlDtdElementList * list)
267 axlDtdElementListNode * node;
269 node = axl_new (axlDtdElementListNode, 1);
271 /* create a node element reference */
272 if (node_name != NULL) {
273 node->data = node_name;
274 node->type = AXL_ELEMENT_NODE;
275 return node;
278 /* create an element list reference */
279 if (list != NULL) {
280 node->data = list;
281 node->type = AXL_ELEMENT_LIST;
282 return node;
285 /* if another type is requested, return NULL */
286 return NULL;
289 /**
290 * @internal
292 * Support function used to destroy all items stored on a item list.
294 * @param node
296 void __destroy_axl_dtd_element_list (axlDtdElementListNode * node)
298 if (node == NULL)
299 return;
300 /* free the reference to the leaf node if defined */
301 if (node->type == AXL_ELEMENT_NODE)
302 axl_free (node->data);
304 /* do not do nothing if the reference is not element list */
305 if (node->type == AXL_ELEMENT_LIST)
306 axl_dtd_item_list_free (node->data);
308 /* free de node itself */
309 axl_free (node);
310 return;
313 /**
314 * @internal
316 * @brief Support function to \ref axl_dtd_parse which creates a new
317 * empty DTD reference.
320 * @return A newly allocated \ref axlDtd reference.
322 axlDtd * __axl_dtd_new ()
324 axlDtd * dtd;
326 /* create the DTD element and nothing else. The rest of items
327 * created on demand */
328 dtd = axl_new (axlDtd, 1);
330 return dtd;
333 bool __queue_items (axlPointer data, axlPointer _stack)
335 axlStack * stack = _stack;
337 /* queue the data */
338 axl_stack_push (stack, data);
340 /* return false to make the function to not stop */
341 return false;
344 void __axl_dtd_queue_childs (axlStack * stack, axlNode * parent)
346 axlNode * child;
348 /* get the first child */
349 child = axl_node_get_first_child (parent);
350 while (child != NULL) {
352 /* queue the child */
353 axl_stack_push (stack, child);
355 /* get the next child */
356 child = axl_node_get_next (child);
357 } /* end while */
359 return;
362 /**
363 * @internal
365 * Support internal function which allows to queue all items inside an
366 * axlDtdElementList to be checked.
368 * @param stack The stack where all data will be placed.
370 * @param dtd_element_list The dtd element list where the data will be
371 * extracted.
373 void __axl_dtd_queue_items (axlStack * stack, axlList * list)
375 /* call to queue items */
376 axl_list_lookup (list, __queue_items, stack);
378 /* nothing more */
379 return;
382 /**
383 * @internal
385 * Support function which allows to check if the provided two dtd
386 * elements are in fact, parent and child.
388 * DTD element have a parent-child relation based in the fact that the
389 * first define top level xml nodes that are followed, in the form of
390 * childs nodes, by other DTD elements that define more childs, etc...
392 * This function allows to check if the provided parent dtd element
393 * have references inside its content specification that proves that
394 * it is indeed a parent definition.
396 * @param dtd_element_parent The supposed DTD parent element.
397 * @param dtd_element_child The supposedd DTD child element.
399 * @return true if the function can confirm that the parent-child
400 * relation exists, false if not or it could be proved.
402 bool __axl_dtd_get_is_parent (axlDtdElement * dtd_element_parent,
403 axlDtdElement * dtd_element_child)
405 axlStack * stack;
406 axlDtdElementListNode * node;
407 axlDtdElementList * list;
409 /* check for leaf nodes, that, by definition, could be a
410 * parent of nothing. */
411 if (dtd_element_parent->list == NULL || dtd_element_parent->list->itemList == NULL) {
412 return false;
415 /* prepare all elements inside the stack to be checked */
416 stack = axl_stack_new (NULL);
417 __axl_dtd_queue_items (stack, dtd_element_parent->list->itemList);
420 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "stack size to operate: %d, list: %d",
421 axl_stack_size (stack),
422 axl_list_length (dtd_element_parent->list->itemList));
424 /* now search for a content particule that makes are reference
425 * to the child DTD element */
426 do {
427 node = axl_stack_pop (stack);
428 switch (node->type) {
429 case AXL_ELEMENT_NODE:
430 /* leaf node case */
431 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a leaf node, checking it");
433 /* seems this is a final node */
434 if (axl_cmp (node->data, dtd_element_child->name)) {
435 /* seems that the content
436 * specification makes a reference to
437 * the child node. */
438 axl_stack_free (stack);
439 return true;
441 break;
442 case AXL_ELEMENT_LIST:
443 /* a nested list case */
444 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a complex node queuing its internal elements, while checking parent=%s for child=%s",
445 dtd_element_parent->name, dtd_element_child->name);
446 /* the item list read, is a complex value,
447 * queue all items inside to be inspected */
448 list = node->data;
449 __axl_dtd_queue_items (stack, list->itemList);
450 break;
451 case AXL_ELEMENT_NOT_DEFINED:
452 /* do nothing */
453 break;
456 /* iterate until all elements are evaluated */
457 }while (! axl_stack_is_empty (stack));
459 /* deallocates no longer used stack */
460 axl_stack_free (stack);
462 /* either it isn't the parent or it can't be proved. */
463 return false;
467 /**
468 * @internal
470 * Support function which allows to get which is the most top root
471 * node for the provided set of DTD elements.
473 axlDtdElement * __axl_dtd_get_new_root (axlDtd * dtd)
475 int iterator;
476 bool change_detected;
478 axlDtdElement * dtd_element_aux;
479 axlDtdElement * dtd_element_the_root_is_on_fire;
481 /* set the very first root node */
482 dtd_element_the_root_is_on_fire = axl_list_get_nth (dtd->elements, 0);
484 do {
485 /* check which is the top */
486 iterator = 0;
487 change_detected = false;
488 while (iterator < axl_list_length (dtd->elements)) {
490 /* get the next reference */
491 dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
493 /* check which is the top */
494 if (__axl_dtd_get_is_parent (dtd_element_aux,
495 dtd_element_the_root_is_on_fire)) {
496 /* it seems that the new element is the root
497 * one, update the reference */
498 dtd_element_the_root_is_on_fire = dtd_element_aux;
499 change_detected = true;
502 /* update inner loop iterator */
503 iterator ++;
504 } /* while end */
505 }while (change_detected);
507 /* return the root found */
508 return dtd_element_the_root_is_on_fire;
511 /**
512 * @internal
514 * @brief Adds the axlDtdElement into the given axlDtd definition,
515 * checking that everything is properly configured, and ensuring that
516 * the root element gets properly configured.
518 * @param dtd The \ref axlDtd object that will receive the
519 * axlDtdElement.
521 * @param stream The \ref axlStream object that will be destroyed if
522 * something wrong is found.
524 * @param element The axlDtdElement to be added to the give axlDtd
525 * object.
527 * @return true if the given axlDtdElement is compatible inside
528 * the axlDtd declaration or false if a error is found.
530 bool __axl_dtd_add_element (axlDtd * dtd, axlDtdElement * element,
531 axlStream * stream, axlError ** error)
533 int iterator = 0;
534 axlDtdElement * dtd_element_aux = NULL;
536 /* check that there is no element already named like the
537 * element received. If it is the case drop an error */
538 while (iterator < axl_list_length (dtd->elements)) {
539 dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
540 if (axl_cmp (dtd_element_aux->name, element->name)) {
541 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD element for <%s> == <%s> was defined twice",
542 dtd_element_aux->name, element->name);
544 axl_error_new (-1, "Find that an DTD element was defined twice (no more than one time is allowed)",
545 stream, error);
546 axl_stream_free (stream);
547 return false;
550 /* update current iterator */
551 iterator++;
554 /* add the new DTD element to the list */
555 axl_list_add (dtd->elements, element);
556 return true;
559 /**
560 * @internal
562 * Internal support function which adds the provided content particule
563 * to the dtd item list received. It also perform all operations
564 * required for the chunk_matched option received.
566 * In the case the function fails to do its work, it will deallocate
567 * the stream, filling the error received.
569 * According to the chunk matched value, the function will react
570 * adding the element and configuring current element list.
573 bool __axl_dtd_element_content_particule_add (axlDtdElementList * dtd_item_list,
574 char * string_aux,
575 int chunk_matched,
576 axlStream * stream,
577 axlError **error)
579 axlDtdElementListNode * node;
581 /* check if the item list was creted or not */
582 if (dtd_item_list->itemList == NULL) {
583 dtd_item_list->itemList = axl_list_new (axl_list_always_return_1,
584 (axlDestroyFunc) __destroy_axl_dtd_element_list);
587 /* create the node to be added */
588 node = __create_axl_dtd_element_list (string_aux, NULL);
590 /* know add the element found */
591 axl_list_add (dtd_item_list->itemList, node);
593 /* set configuration for item repetition */
594 switch (chunk_matched) {
595 case 4:
596 /* one or many times */
597 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one to many repeat pattern: (+)");
598 node->times = ONE_OR_MANY;
599 break;
600 case 5:
601 /* zero or many times */
602 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting zero to many repeat pattern: (*)");
603 node->times = ZERO_OR_MANY;
604 break;
605 case 6:
606 /* zero or one time */
607 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one to one repeat pattern: (?)");
608 node->times = ZERO_OR_ONE;
609 break;
610 default:
611 /* one and only one time */
612 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "setting one and only one repeat pattern: ()");
613 node->times = ONE_AND_ONLY_ONE;
616 /* return that all is ok */
617 return true;
621 /**
622 * @internal
624 * @brief Support function which allows to get current repetition
625 * configuration.
627 * @param stream The stream where the operation will be performed.
629 * @return Current configuration read, the function will properly work
630 * if it is called when it is espected to find a content specification
631 * repetition. If not found, the \ref ONE_AND_ONLY_ONE is returned.
633 AxlDtdTimes __axl_dtd_get_repetition_conf (axlStream * stream)
635 axl_return_val_if_fail (stream, ONE_AND_ONLY_ONE);
637 if (axl_stream_inspect (stream, "?", 1) > 0) {
639 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '?' repetition conf");
640 /* seems the content specification could appear zero
641 * or one time */
642 return ZERO_OR_ONE;
644 } else if (axl_stream_inspect (stream, "+", 1) > 0) {
646 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '+' repetition conf");
647 /* seems the content specification must appear one up
648 * to many */
649 return ONE_OR_MANY;
651 } else if (axl_stream_inspect (stream, "*", 1) > 0) {
653 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found '*' repetition conf");
654 /* seems the content specification could appear zero
655 * up to many */
656 return ZERO_OR_MANY;
659 /* the content specification must appear */
660 return ONE_AND_ONLY_ONE;
663 /**
664 * @internal
666 * Support function which creates a child item list, insert it to the
667 * parent item list received.
669 * @param parent
671 * @return
673 axlDtdElementList * __axl_dtd_create_and_queue (axlDtdElementList * parent)
675 axlDtdElementList * child;
676 axlDtdElementListNode * node;
678 /* create the DTD item list */
679 child = axl_new (axlDtdElementList, 1);
681 /* make by default the item list to be defined as "not
682 * defined" until the first separator is found */
683 child->type = STILL_UNDEF;
685 /* create a node that */
686 node = __create_axl_dtd_element_list (NULL, child);
688 /* create the parent list reference if weren't */
689 if (parent->itemList == NULL) {
690 parent->itemList = axl_list_new (axl_list_always_return_1,
691 (axlDestroyFunc) __destroy_axl_dtd_element_list);
694 /* add the node */
695 axl_list_add (parent->itemList, node);
697 /* return the new child created */
698 return child;
701 /**
702 * @internal
704 * Updates current chunk readed information to allow perform a better
705 * code after calling this function.
708 void __axl_dtd_element_spec_update_chunk_matched (axlStream * stream,
709 int * chunk_matched)
711 /* check for for sequence or choice characters */
712 if (axl_stream_inspect (stream, ",", 1) > 0) {
713 /* flag that we have found a , (choice)
714 * separator */
715 (*chunk_matched) = 1;
717 } else if (axl_stream_inspect (stream, "|", 1) > 0) {
718 /* flag that we have found a | (sequence)
719 * separator */
720 (*chunk_matched) = 2;
722 } else if (axl_stream_inspect (stream, ")", 1) > 0) {
723 /* flag that we have found a | (sequence)
724 * separator */
725 (*chunk_matched) = 3;
727 } else if (axl_stream_inspect (stream, "+", 1) > 0) {
728 /* flag that we have found a | (sequence)
729 * separator */
730 (*chunk_matched) = 4;
732 } else if (axl_stream_inspect (stream, "*", 1) > 0) {
733 /* flag that we have found a | (sequence)
734 * separator */
735 (*chunk_matched) = 5;
737 } else if (axl_stream_inspect (stream, "?", 1) > 0) {
738 /* flag that we have found a | (sequence)
739 * separator */
740 (*chunk_matched) = 6;
743 return;
746 /**
747 * @internal
749 * Support function to read the content particule separator once the
750 * repeat pattern was found
753 bool __axl_dtd_element_spec_update_chunk_matched_for_cp_separator (axlStream * stream,
754 int * chunk_matched)
756 /* consume previous white spaces */
757 AXL_CONSUME_SPACES (stream);
759 /* check for for sequence or choice characters */
760 if (axl_stream_inspect (stream, ",", 1) > 0) {
761 /* flag that we have found a , (choice)
762 * separator */
763 (*chunk_matched) = 1;
764 return true;
766 } else if (axl_stream_inspect (stream, "|", 1) > 0) {
767 /* flag that we have found a | (sequence)
768 * separator */
769 (*chunk_matched) = 2;
770 return true;
772 } else if (axl_stream_inspect (stream, ")", 1) > 0) {
773 /* flag that we have found a | (sequence)
774 * separator */
775 (*chunk_matched) = 3;
776 return true;
779 return false;
782 /**
783 * @internal
785 * Support function which allows to read the next content particule.
787 char * __axl_dtd_read_content_particule (axlStream * stream,
788 int * chunk_matched,
789 axlStack * dtd_item_stack,
790 axlError ** error)
792 char * string_aux;
794 /* read the spec particule stopping when a white space
795 * or other character is found */
796 string_aux = axl_stream_get_until (stream, NULL, chunk_matched, true, 8,
797 /* basic, default delimiters: 0, 1, 2, 3 */
798 " ", ",", "|", ")",
799 /* repetition configuration: 4, 5, 6 */
800 "+", "*", "?",
801 /* new dtd item list being opened: 8 */
802 "(");
803 if (string_aux == NULL) {
804 axl_error_new (-1, "Expected to find a element content specification particule, but it wasn't found",
805 stream, error);
806 axl_stack_free (dtd_item_stack);
807 axl_stream_free (stream);
808 return NULL;
811 /* check the user doesn't nest item list in a not
812 * proper way */
813 if (*chunk_matched == 8) {
814 axl_error_new (-1, "Found a not proper nesting item list for a DTD element, before using ( a separator must be used (CHOICE: |, SEQUENCE: ,)",
815 stream, error);
816 axl_stack_free (dtd_item_stack);
817 axl_stream_free (stream);
818 return NULL;
821 /* nullify stream internal reference */
822 axl_stream_nullify (stream, LAST_CHUNK);
824 /* return the content particule found */
825 return string_aux;
828 /**
829 * @internal
831 * Support function which reads current <!ELEMENT specification,
832 * configuring it to the received axlDtdElement.
834 * @param stream The stream where the axlDtdElement spec will be read.
836 * @param dtd_element The axlDtdElement that will receive the content
837 * spec.
839 * @param error An optional \ref axlError, where errors will be
840 * reported.
842 * @return \ref true if the content spec was properly read or \ref
843 * false if not.
845 bool __axl_dtd_read_element_spec (axlStream * stream, axlDtdElement * dtd_element, axlError ** error)
847 char * string_aux;
848 bool is_pcdata;
849 int chunk_matched = -1;
850 axlStack * dtd_item_stack;
851 axlDtdElementList * dtd_item_list;
852 bool is_empty;
855 /* create the stack used to control which is
856 * the current context for the items read for
857 * the xml DTD especification (pd, pd2, (pr|po), ..) */
858 dtd_item_stack = axl_stack_new (NULL);
860 /* create the DTD item list */
861 dtd_item_list = axl_new (axlDtdElementList, 1);
863 /* by default, set still undef to change it once a separator
864 * is detected or the function ends. This will help to detect
865 * problems produced by people mixing content element
866 * separators. */
867 dtd_item_list->type = STILL_UNDEF;
869 /* set the content spec list to the dtd element read */
870 dtd_element->list = dtd_item_list;
872 /* push the item created */
873 /* axl_stack_push (dtd_item_stack, dtd_item_list); */
875 /* consume previous white spaces */
876 AXL_CONSUME_SPACES (stream);
878 /* check that the content specification have an ( */
879 if (! (axl_stream_inspect (stream, "(", 1))) {
880 axl_error_new (-1, "Expected to find a element content specification opener \"(\", but it wasn't found",
881 stream, error);
882 axl_stack_free (dtd_item_stack);
883 axl_stream_free (stream);
884 return false;
887 do {
888 /* consume previous white spaces */
889 AXL_CONSUME_SPACES (stream);
891 /* a new item list have been opened */
892 if (axl_stream_inspect (stream, "(", 1) > 0) {
894 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a DTD item list openining: %d",
895 axl_stack_size (dtd_item_stack));
897 /* a new item list is being defined, we have
898 * to queue current dtd_item_list and create a
899 * new item list */
900 axl_stack_push (dtd_item_stack, dtd_item_list);
902 /* create the DTD item list */
903 dtd_item_list = __axl_dtd_create_and_queue (dtd_item_list);
905 /* let's continue at the begining */
906 continue;
910 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "iterating again to get a new content particule (item list size: %d)",
911 axl_dtd_item_list_count (dtd_item_list));
913 /* read the next content particule: here is the chunk
914 * matched codes found:
915 * basic, default delimiters:
916 * 0, 1, 2, 3 -> " ", ",", "|", ")"
917 * repetition configuration:
918 * 4, 5, 6 -> "+", "*", "?",
919 * new dtd item list being opened:
920 * 8 -> "(" */
921 string_aux = __axl_dtd_read_content_particule (stream, &chunk_matched, dtd_item_stack, error);
922 if (string_aux == NULL)
923 return false;
925 /* check, and record, that the string read is
926 * PCDATA */
927 is_pcdata = axl_cmp (string_aux, "#PCDATA");
929 /* add the item read if have something defined */
931 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found content spec particule: (size: %d) '%s'",
932 strlen (string_aux),
933 string_aux);
935 /* check if the have matched a white space: next check is
936 * based on the call to axl_stream_get_until at the caller
937 * function: " " */
938 if (chunk_matched == 0) {
940 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
941 "found white spaces as delimiter, consuming them (current chunk matched: %d)",
942 chunk_matched);
944 /* consume previous white spaces */
945 AXL_CONSUME_SPACES (stream);
947 /* update current chunk_matched to conform to
948 * an stream that have all elements really
949 * close: the following function tries to read
950 * and update chunk_matched variable to point
951 * to the value read for ",", "|", "+", "*",
952 * "?" and ")" because white spaces were found */
953 __axl_dtd_element_spec_update_chunk_matched (stream, &chunk_matched);
956 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
957 "current chunk matched before update (%d)",
958 chunk_matched);
961 /* add the content particule found, this function
962 * already detect that a white space was found and
963 * consumes all white spaces found */
964 if (!__axl_dtd_element_content_particule_add (dtd_item_list, string_aux, chunk_matched, stream, error))
965 return false;
967 if (chunk_matched == 4 || chunk_matched == 5 || chunk_matched == 6) {
968 /* found a repetition pattern */
969 if (! __axl_dtd_element_spec_update_chunk_matched_for_cp_separator (stream, &chunk_matched)) {
970 axl_error_new (-1, "Before a repetition pattern (*,+,?) expected to find a content particule separator",
971 stream, error);
972 axl_stack_free (dtd_item_stack);
973 axl_stream_free (stream);
974 return false;
978 /* set current sequence type accoring to separators
979 * used */
980 switch (chunk_matched) {
981 case 1:
982 if (dtd_item_list->type == CHOICE) {
983 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 (|)",
984 stream, error);
985 axl_stack_free (dtd_item_stack);
986 axl_stream_free (stream);
987 return false;
989 dtd_item_list->type = SEQUENCE;
990 break;
991 case 2:
992 if (dtd_item_list->type == SEQUENCE) {
993 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 (,)",
994 stream, error);
995 axl_stack_free (dtd_item_stack);
996 axl_stream_free (stream);
997 return false;
999 dtd_item_list->type = CHOICE;
1000 break;
1003 /* set element type if a element list terminator was
1004 * found ( 3 = ')' = chunk_matched) */
1005 if ((chunk_matched == 3) && is_pcdata) {
1006 if (axl_list_length (dtd_item_list->itemList) == 1)
1007 dtd_element->type = ELEMENT_TYPE_PCDATA;
1008 else if (axl_list_length (dtd_item_list->itemList) > 1)
1009 dtd_element->type = ELEMENT_TYPE_MIXED;
1012 /* pop current element list header */
1013 if (chunk_matched == 3) {
1014 do {
1015 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a DTD item list termination: stack status: %d",
1016 axl_stack_size (dtd_item_stack));
1017 /* consume previous white spaces */
1018 AXL_CONSUME_SPACES (stream);
1019 dtd_item_list->times = __axl_dtd_get_repetition_conf (stream);
1021 /* consume previous white spaces */
1022 AXL_CONSUME_SPACES (stream);
1024 if (axl_stream_inspect (stream, ",", 1) > 0) {
1025 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a sequence (,) separator while reading terminator list");
1027 chunk_matched = 1;
1029 else if (axl_stream_inspect (stream, "|", 1) > 0) {
1030 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a choice (|) separator while reading terminator list");
1031 chunk_matched = 2;
1034 /* this means that a ) was found, we have to
1035 * pop current queue */
1036 is_empty = axl_stack_is_empty (dtd_item_stack);
1037 if (! is_empty) {
1038 dtd_item_list = axl_stack_pop (dtd_item_stack);
1039 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting the next item list in the stack, stack status: %d",
1040 axl_stack_size (dtd_item_stack));
1043 /* special case: check if the next element to
1044 * be read is a new ) */
1045 /* consume previous white spaces */
1046 AXL_CONSUME_SPACES (stream);
1048 }while ((axl_stream_inspect (stream, ")", 1) > 0) && !is_empty);
1050 /* drop a log */
1051 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "terminator sequence status: chunk matched=%d ans stack status: %d",
1052 chunk_matched, axl_stack_size (dtd_item_stack));
1056 /* check if we have finished */
1057 } while (chunk_matched != 3 || (! axl_stack_is_empty (dtd_item_stack)));
1060 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "content spec terminated, now lookup for the termination");
1062 /* consume previous white spaces */
1063 /* AXL_CONSUME_SPACES (stream);*/
1065 /* read here repetition specification */
1066 /* dtd_item_list->times = __axl_dtd_get_repetition_conf (stream); */
1068 /* set default content element separator */
1069 if (dtd_item_list->type == STILL_UNDEF)
1070 dtd_item_list->type = SEQUENCE;
1072 /* free the stack used */
1073 axl_stack_free (dtd_item_stack);
1076 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD content element specification found and parsed ok");
1079 /* content spec readed properly */
1080 return true;
1083 /**
1084 * @internal
1086 * Calculates the number of nodes to be matched at minimum for the
1087 * provided DTD element.
1089 * @param element The DTD element to configure with its minimum item
1090 * count to be matched.
1092 int __axl_dtd_parse_element_get_compulsory_num (axlDtdElementList * list)
1094 axlDtdElementListNode * itemNode;
1095 int count = 0;
1096 int iterator = 0;
1098 /* check for null parameters */
1099 if (list == NULL)
1100 return 0;
1102 /* only count for repetitiong patterns that makes obligatory
1103 * to have childs */
1104 if (list->times == ONE_AND_ONLY_ONE ||
1105 list->times == ONE_OR_MANY) {
1107 while (iterator < axl_list_length (list->itemList)) {
1108 /* get the reference for the item node */
1109 itemNode = axl_list_get_nth (list->itemList, iterator);
1111 /* check if the repetitiong patter is
1112 * compulsory */
1113 if (itemNode->times == ONE_OR_MANY ||
1114 itemNode->times == ONE_AND_ONLY_ONE) {
1115 /* check if we have an itemNode that has an
1116 * Node or a list */
1117 if (itemNode->type == AXL_ELEMENT_NODE) {
1118 /* we have an item node */
1119 count++;
1120 if (list->type == CHOICE) {
1121 /* because we have a
1122 * choice list, once
1123 * validated one item,
1124 * it is the minimum
1125 * requirement. */
1126 return count;
1128 } else {
1129 /* we have a list */
1130 count += __axl_dtd_parse_element_get_compulsory_num (itemNode->data);
1134 /* update the index */
1135 iterator++;
1139 /* return current count */
1140 return count;
1144 /**
1145 * @internal
1147 * Parses a document type element that it is expected to be found at
1148 * the given stream.
1150 * @param dtd The axlDtd where the element type readed must be added.
1152 * @param stream The stream where the element type if expected to be found.
1154 * @param error An axlError, optional, reference where error will be
1155 * reported.
1157 * @return true if the element was parsed properly, false if
1158 * not. The stream associated will be unrefered and the axlError
1159 * provided will be filled if an error is found.
1161 bool __axl_dtd_parse_element (axlDtd * dtd, axlStream * stream, axlError ** error)
1163 char * string_aux;
1164 int matched_chunk = -1;
1165 axlDtdElement * element;
1167 /* init the dtd element list */
1168 if (dtd->elements == NULL)
1169 dtd->elements = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_element_free);
1171 /* consume previous white spaces */
1172 AXL_CONSUME_SPACES (stream);
1174 /* get for the first element declaration */
1175 if (! (axl_stream_inspect (stream, "<!ELEMENT", 9) > 0)) {
1176 axl_error_new (-1, "Expected to receive a <!ELEMENT, but it wasn't found", stream, error);
1177 axl_stream_free (stream);
1178 return false;
1181 /* consume previous white spaces */
1182 AXL_CONSUME_SPACES (stream);
1184 /* get the element name */
1185 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, false, 3, ">", "(", " ", "<!ELEMENT");
1186 if (string_aux == NULL) {
1187 axl_error_new (-1, "Expected to receive a DTD element name for <!ELEMENT declaration, but not found", stream, error);
1188 axl_stream_free (stream);
1189 return false;
1192 /* check that the DTD have an element name and an element type */
1193 if ((matched_chunk == 0) || (matched_chunk == 3)) {
1194 axl_error_new (-1, "Found a DTD <!ELEMENT declaration, without content specification. Missing value, examples: EMPTY, ANY, (..)", stream, error);
1195 axl_stream_free (stream);
1196 return false;
1199 /* nullify internal stream content */
1200 axl_stream_nullify (stream, LAST_CHUNK);
1202 /* create the DTD element */
1203 element = axl_new (axlDtdElement, 1);
1204 element->name = string_aux;
1206 /* consume previous white spaces */
1207 AXL_CONSUME_SPACES (stream);
1209 /* now, check for the basic cases: ANY and EMPTY */
1210 if (axl_stream_peek (stream, "EMPTY", 5) > 0) {
1211 /* accept previous peek */
1212 axl_stream_accept (stream);
1214 /* found empty declaration */
1215 element->type = ELEMENT_TYPE_EMPTY;
1217 } else if (axl_stream_peek (stream, "ANY", 3) > 0) {
1218 /* accept previous peek */
1219 axl_stream_accept (stream);
1221 /* found any declaration */
1222 element->type = ELEMENT_TYPE_ANY;
1223 } else {
1224 /* complex element type declaration, let's roll now
1225 * get the element content type read current dtd
1226 * element spec.
1228 * By default, any comple element type definition,
1229 * have childrens, until PC data definition is found,
1230 * which leads to the two possible values: Mixed and
1231 * PcData */
1232 element->type = ELEMENT_TYPE_CHILDREN;
1233 if (!__axl_dtd_read_element_spec (stream, element, error))
1234 return false;
1237 /* add element found */
1238 if (! __axl_dtd_add_element (dtd, element, stream, error))
1239 return false;
1241 /* consume previous white spaces */
1242 AXL_CONSUME_SPACES (stream);
1244 /* check for the last DTD declaration */
1245 if (! (axl_stream_inspect (stream, ">", 1))) {
1246 axl_error_new (-1, "Unable to find last, > terminator for the DTD <!ELEMENT declaration", stream, error);
1247 axl_stream_free (stream);
1248 return false;
1251 /* now, count the number of obligatory elements, required for
1252 * the validation process */
1253 element->minimum_match = __axl_dtd_parse_element_get_compulsory_num (element->list);
1255 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD element declaration read complete: minimum matching elements: %d",
1256 element->minimum_match);
1258 /* element type declaration completely read */
1259 return true;
1262 /**
1263 * @internal
1265 * Destroy the provided reference and its associated data.
1267 * @param decl The reference declaration.
1269 void axl_dtd_attribute_decl_free (axlDtdAttributeDecl * decl)
1271 /* free the rule name */
1272 if (decl->name != NULL)
1273 axl_free (decl->name);
1275 /* free the default value */
1276 if (decl->default_value != NULL)
1277 axl_free (decl->default_value);
1279 /* free enum declaration list if defined */
1280 if (decl->enumvalues != NULL)
1281 axl_list_free (decl->enumvalues);
1283 /* free the node itself */
1284 axl_free (decl);
1286 /* nothing more to do */
1287 return;
1290 /**
1291 * @internal function to dealloc an single attribute set decleration.
1293 * @param attribute The reference to dealloc.
1295 void axl_dtd_attribute_free (axlDtdAttribute * attribute)
1297 /* free the attribute list, name and the node itself */
1298 axl_free (attribute->name);
1299 axl_list_free (attribute->list);
1300 axl_free (attribute);
1302 return;
1305 bool __find_attr_decl (axlPointer _element, axlPointer data)
1307 axlDtdAttributeDecl * decl = _element;
1308 char * name = data;
1310 /* check the name */
1311 if (axl_cmp (decl->name, name))
1312 return true;
1314 /* it is not the element */
1315 return false;
1318 /**
1319 * @brief Allows to check if the stream contains a reference to a
1320 * entity, calling the resolver to get the replacement text to be
1321 * placed.
1323 * @param resolver The function to be called with the replacement
1324 * text. This function must return the replacement text or NULL if it
1325 * fails. Failing to return a reference resolution will make the
1326 * entity reference to appear as is.
1328 * @param resolver The entity reference resolver function to be called
1329 * to solve references found.
1331 * @param data User defined data provided to the function, passed
1332 * directly to the resolver function once executed.
1334 * @param stream The stream where the entity reference could appear.
1336 * @param prefix The reference prefix to recognize. Values allowed
1337 * are: % (DTD references) and & (general entity references).
1339 * @param error Optional reference to the axlError to report textual
1340 * diagnostic errors.
1342 * @return The function return \ref false if some error while
1343 * resolving entity references was found. Otherwise the function
1344 * return true.
1346 bool axl_dtd_check_entity_ref_and_expand (axlDtdEntityResolver resolver,
1347 axlPointer data,
1348 axlStream * stream,
1349 const char * prefix,
1350 axlError ** error)
1353 char * string_aux;
1354 char * new_value;
1355 int index;
1357 /* check if we have an entity reference using the provided prefix */
1358 index = axl_stream_get_index (stream);
1359 if (! (axl_stream_inspect (stream, prefix, 1) > 0))
1360 return true;
1362 /* get the entity reference until the end */
1363 string_aux = axl_stream_get_until (stream, NULL, NULL, true, 1, ";");
1364 if (string_aux == NULL) {
1365 axl_error_new (-1, "null value received while expecting to find the entity reference to resolve.", stream, error);
1366 axl_stream_free (stream);
1367 return false;
1368 } /* end if */
1370 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found entity reference: %s%s;...resolving", prefix, string_aux);
1372 /* resolve the reference */
1373 new_value = (char *) resolver (string_aux, data);
1374 if (new_value == NULL) {
1375 axl_stream_move (stream, index);
1376 return true;
1377 } /* end if */
1379 /* accept content consumed */
1380 axl_stream_accept (stream);
1382 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "entity resolved to: %s", new_value);
1384 /* place the replacement data at the start of the stream */
1385 new_value = axl_strdup_printf ("%s ", new_value);
1386 axl_stream_push (stream, new_value, strlen (new_value));
1387 axl_free (new_value);
1389 return true;
1392 /**
1393 * @internal Entity resolver used by __axl_dtd_parse_attlist.
1395 const char * __axl_dtd_entity_resolver (const char * entityName, axlPointer data)
1397 /* return the entity resolution */
1398 return axl_dtd_entity_value ((axlDtd *) data, entityName, PARAMETER_ENTITY);
1399 } /* end if */
1401 axlList * __axl_dtd_parse_enumvalues (const char * _enum_values)
1403 char ** result;
1404 int iterator;
1405 axlList * list;
1407 result = axl_stream_split (_enum_values, 1, "|");
1408 iterator = 0;
1409 list = axl_list_new (axl_list_always_return_1, axl_free);
1412 while (result[iterator]) {
1413 /* clean the enum value */
1414 axl_stream_trim (result[iterator]);
1416 /* add to the list */
1417 axl_list_add (list, axl_strdup (result[iterator]));
1419 /* update the iterator value */
1420 iterator++;
1422 } /* end while */
1424 /* free tokens */
1425 axl_stream_freev (result);
1427 /* return the list */
1428 return list;
1431 /**
1432 * @internal function used by \ref axl_dtd_attr_validation function to
1433 * lookup ATTLIST contraints flaged as unique ID.
1435 bool __find_id_decl (axlPointer _element, axlPointer data)
1437 /* return the comparision */
1438 return (((axlDtdAttributeDecl *) _element)->type == TOKENIZED_TYPE_ID);
1440 } /* end __find_id_decl */
1443 /**
1444 * @internal
1446 * Parse the <!ATTLIST decleration, registering it into the provided
1447 * dtd element.
1449 bool __axl_dtd_parse_attlist (axlDtd * dtd, axlStream * stream, axlError ** error)
1451 char * string_aux = NULL;
1452 int matched_chunk = -1;
1453 axlDtdAttribute * attribute = NULL;
1454 axlDtdAttributeDecl * decl = NULL;
1455 axlDtdAttributeDecl * declAux = NULL;
1456 char * err_msg;
1458 /* init the dtd attr list */
1459 if (dtd->attributes == NULL)
1460 dtd->attributes = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_attribute_free);
1462 /* consume previous white spaces */
1463 AXL_CONSUME_SPACES (stream);
1465 /* get the element name */
1466 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, false, 1, " ");
1467 if (string_aux == NULL) {
1468 axl_error_new (-1, "Expected to receive a DTD attribute name for <!ATTLIST declaration, but not found", stream, error);
1469 axl_stream_free (stream);
1470 return false;
1473 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found dtd attr declaration for node: <%s>", string_aux);
1475 /* find the node that holds all attr declarations for the node found */
1476 attribute = axl_dtd_get_attr (dtd, string_aux);
1478 /* check if found */
1479 if (attribute == NULL) {
1480 /* create the axlDtdAttribute holder */
1481 attribute = axl_new (axlDtdAttribute, 1);
1483 /* record the node to which the list of rules applies */
1484 axl_stream_nullify (stream, LAST_CHUNK);
1485 attribute->name = string_aux;
1487 /* init the attribute rule list */
1488 attribute->list = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_attribute_decl_free);
1490 /* now configure this new attribute inside the dtd */
1491 axl_list_add (dtd->attributes, attribute);
1492 } /* end if */
1494 /* now get the list of attributes */
1495 while (1) {
1496 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "finding next att declaration");
1498 /* consume previous white spaces */
1499 AXL_CONSUME_SPACES (stream);
1501 /* check if we have finished */
1502 if (axl_stream_inspect (stream, ">", 1) > 0)
1503 break;
1505 /* get the attribute name the rules applies */
1506 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, false, 1, " ");
1507 if (string_aux == NULL) {
1508 axl_error_new (-1, "Expected to receive an attribute name for <!ATTLIST declaration, but not found", stream, error);
1509 axl_stream_free (stream);
1510 return false;
1513 /* nully the string and store it new rule created */
1514 axl_stream_nullify (stream, LAST_CHUNK);
1516 /* create a new attribute single constraint */
1517 decl = axl_new (axlDtdAttributeDecl, 1);
1518 decl->name = string_aux;
1520 /* add the attribute constraint to the list */
1521 axl_list_add (attribute->list, decl);
1523 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "find constraint for attribute name=%s", decl->name);
1525 /* consume previous white spaces */
1526 AXL_CONSUME_SPACES (stream);
1528 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking constraint type..");
1530 /* check for an entity reference and expand the stream
1531 * content with its resolution */
1532 if (! axl_dtd_check_entity_ref_and_expand (__axl_dtd_entity_resolver, dtd, stream, "%", error))
1533 return false;
1535 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "about to check attr constraint type, stream status: '%s'",
1536 axl_stream_get_following (stream, 30));
1538 /* now check the contraint type */
1539 if (axl_stream_inspect (stream, "NOTATION", 8) > 0) {
1540 /* parse notation declaration */
1541 }else if (axl_stream_inspect (stream, "(", 1) > 0) {
1542 /* parse enum declaration */
1543 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, true, 1, ")");
1544 if (string_aux == NULL) {
1545 axl_error_new (-1, "expected to find enum declaration but termination caracter ')' was not found", stream, error);
1546 axl_stream_free (stream);
1547 return false;
1548 } /* end if */
1549 decl->type = ENUMERATION_TYPE;
1550 decl->enumvalues = __axl_dtd_parse_enumvalues (string_aux);
1551 }else {
1552 /* set the attribute type */
1553 if (axl_stream_inspect (stream, "CDATA", 5) > 0) {
1554 decl->type = CDATA_ATTRIBUTE;
1555 } else if (axl_stream_inspect (stream, "IDREFS", 6) > 0) {
1557 /* flag the type */
1558 decl->type = TOKENIZED_TYPE_IDREFS;
1560 /* flag the dtd to have a IDREF declaration */
1561 dtd->haveIdRefDecl = true;
1562 } else if (axl_stream_inspect (stream, "IDREF", 5) > 0) {
1563 /* notify type found */
1564 decl->type = TOKENIZED_TYPE_IDREF;
1566 /* flag the dtd to have a IDREF declaration */
1567 dtd->haveIdRefDecl = true;
1569 } else if (axl_stream_inspect (stream, "ID", 2) > 0) {
1571 /* notify the type found */
1572 decl->type = TOKENIZED_TYPE_ID;
1574 /* flag the dtd to have a ID declaration */
1575 dtd->haveIdDecl = true;
1577 } else if (axl_stream_inspect (stream, "ENTITY", 6) > 0)
1578 decl->type = TOKENIZED_TYPE_ENTITY;
1579 else if (axl_stream_inspect (stream, "ENTITIES", 8) > 0)
1580 decl->type = TOKENIZED_TYPE_ENTITIES;
1581 else if (axl_stream_inspect (stream, "NMTOKENS", 8) > 0)
1582 decl->type = TOKENIZED_TYPE_NMTOKENS;
1583 else if (axl_stream_inspect (stream, "NMTOKEN", 7) > 0)
1584 decl->type = TOKENIZED_TYPE_NMTOKEN;
1585 else {
1586 axl_error_new (-1, "Unrecognied attr type declaration found, check your <!ATTLIST declaration", stream, error);
1587 axl_stream_free (stream);
1588 return false;
1589 } /* end if */
1590 } /* end if */
1592 /* consume previous white spaces */
1593 AXL_CONSUME_SPACES (stream);
1595 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking default value declaration, stream status: '%s'",
1596 axl_stream_get_following (stream, 30));
1598 /* get default declaration value */
1599 if (axl_stream_inspect (stream, "#REQUIRED", 9) > 0) {
1600 decl->defaults = ATT_REQUIRED;
1601 } else if (axl_stream_inspect (stream, "#IMPLIED", 8) > 0) {
1602 decl->defaults = ATT_IMPLIED;
1603 } else {
1604 decl->defaults = ATT_IMPLIED;
1605 if (axl_stream_inspect (stream, "#FIXED", 6) > 0) {
1606 decl->defaults = ATT_FIXED;
1608 /* consume previous white spaces */
1609 AXL_CONSUME_SPACES (stream);
1612 /* check default value for this case */
1613 if (! (axl_stream_peek (stream, "\"", 1) > 0 ||
1614 axl_stream_peek (stream, "'", 1) > 0)) {
1615 err_msg = axl_strdup_printf ("Unable to find default attribute declaration (#REQUIRED, #IMPLIED, #FIXED) for attribute %s, node <%s>",
1616 decl->name, attribute->name);
1617 axl_error_new (-1, err_msg, stream, error);
1618 axl_stream_free (stream);
1619 axl_free (err_msg);
1620 return false;
1621 } /* end if */
1622 } /* end if */
1624 /* check constraint for ID types */
1625 if (decl->type == TOKENIZED_TYPE_ID) {
1626 /* check that the node doesn't have any unique
1627 * id declared */
1629 /* check if the node have TOKENIZED_TYPE_ID */
1630 declAux = axl_list_lookup (attribute->list, __find_id_decl, NULL);
1631 if (declAux != NULL && !axl_cmp (declAux->name, decl->name)) {
1632 err_msg = axl_strdup_printf ("Found ATTLIST declaration, with several ID declarations <ATTLIST %s %s..",
1633 attribute->name, decl->name);
1634 axl_error_new (-1, err_msg, stream, error);
1635 axl_stream_free (stream);
1636 axl_free (err_msg);
1637 return false;
1638 } /* end if */
1640 /* check required and implied */
1641 if (decl->defaults != ATT_REQUIRED && decl->defaults != ATT_IMPLIED) {
1642 err_msg = axl_strdup_printf ("Found ATTLIST declaration, with ID, that don't have configured either #IMPLICIT or #REQUIRED for attribute %s, node <%s>",
1643 decl->name, attribute->name);
1644 axl_error_new (-1, err_msg, stream, error);
1645 axl_stream_free (stream);
1646 axl_free (err_msg);
1647 return false;
1648 } /* end if */
1649 } /* end if */
1651 /* consume previous white spaces */
1652 AXL_CONSUME_SPACES (stream);
1654 /* nullify to check this value later */
1655 string_aux = NULL;
1656 if (axl_stream_inspect (stream, "\"", 1) > 0) {
1657 /* get until */
1658 string_aux = axl_stream_get_until (stream, NULL, NULL, true, 1, "\"");
1659 } else if (axl_stream_inspect (stream, "'", 1) > 0) {
1660 /* get until */
1661 string_aux = axl_stream_get_until (stream, NULL, NULL, true, 1, "\"");
1662 } /* end if */
1664 /* check if default value was found */
1665 if (string_aux != NULL) {
1667 /* found default value, check if we have an
1668 * enumeration type, enforcing that the value
1669 * defined to be inside the enumeration */
1670 if (decl->type == ENUMERATION_TYPE) {
1671 if (axl_list_lookup (decl->enumvalues, axl_list_find_string, string_aux) == NULL) {
1672 axl_error_new (-1,
1673 "Configured a default value for an attribute list which only accepts a set of enum values that do not containt it.",
1674 stream, error);
1675 axl_stream_free (stream);
1676 return false;
1677 } /* end if */
1678 } /* end if */
1680 /* nullify value and make string_aux to be
1681 * owned by the axlDtdAttributeDecl
1682 * reference */
1683 axl_stream_nullify (stream, LAST_CHUNK);
1684 decl->default_value = string_aux;
1685 } /* end if */
1687 } /* end while */
1689 /* properly parsed */
1690 return true;
1693 /**
1694 * @internal
1696 * Destroy the provided entity reference and all allocated memory.
1698 * @param entity The entity the deallocate.
1700 void axl_dtd_entity_free (axlDtdEntity * entity)
1702 /* the entity reference */
1703 axl_return_if_fail (entity);
1705 /* free the entity name */
1706 if (entity->name)
1707 axl_free (entity->name);
1709 /* free the content */
1710 if (entity->content)
1711 axl_free (entity->content);
1713 /* free external data if defined */
1714 if (entity->data) {
1715 /* free system literal */
1716 if (entity->data->system_literal)
1717 axl_free (entity->data->system_literal);
1719 /* free public literal */
1720 if (entity->data->public_literal)
1721 axl_free (entity->data->public_literal);
1723 /* free ndata literal */
1724 if (entity->data->ndata)
1725 axl_free (entity->data->ndata);
1727 /* free the node itself */
1728 axl_free (entity->data);
1731 /* free the node */
1732 axl_free (entity);
1734 return;
1737 /**
1738 * @internal
1740 * Parses an entity definition from the current status of the stream
1741 * provided.
1743 bool __axl_dtd_parse_entity (axlDtd * dtd, axlStream * stream, axlError ** error)
1745 char * string_aux;
1746 int matched_chunk;
1747 axlDtdEntity * entity;
1749 /* init the dtd element list */
1750 if (dtd->entities == NULL)
1751 dtd->entities = axl_list_new (axl_list_always_return_1, (axlDestroyFunc) axl_dtd_entity_free);
1753 /* consume previous white spaces */
1754 AXL_CONSUME_SPACES (stream);
1756 /* get for the first element declaration */
1757 if (! (axl_stream_inspect (stream, "<!ENTITY", 8) > 0)) {
1758 axl_error_new (-1, "Expected to receive a <!ENTITY, but it wasn't found", stream, error);
1759 axl_stream_free (stream);
1760 return false;
1763 /* consume previous white spaces */
1764 AXL_CONSUME_SPACES (stream);
1766 /* create a new entity */
1767 entity = axl_new (axlDtdEntity, 1);
1769 /* set the entity and return true */
1770 axl_list_add (dtd->entities, entity);
1772 /* check for parameter entity definition */
1773 if (axl_stream_inspect (stream, "%", 1) > 0) {
1774 /* set the entity type */
1775 entity->type = PARAMETER_ENTITY;
1777 /* consume previous white spaces */
1778 AXL_CONSUME_SPACES (stream);
1780 } else
1781 entity->type = GENERAL_ENTITY;
1783 /* get the element name */
1784 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, false, 1, " ");
1785 if (string_aux == NULL) {
1786 axl_error_new (-1, "Expected to receive a DTD entity name for <!ENTITY declaration, but not found", stream, error);
1787 axl_stream_free (stream);
1788 return false;
1791 /* set the name */
1792 axl_stream_nullify (stream, LAST_CHUNK);
1793 entity->name = string_aux;
1795 /* consume previous white spaces */
1796 AXL_CONSUME_SPACES (stream);
1798 /* now check if we have an external reference */
1799 if (axl_stream_inspect (stream, "PUBLIC", 6) > 0) {
1800 /* we have a public external resource definition */
1802 }else if (axl_stream_inspect (stream, "SYSTEM", 6) > 0) {
1803 /* we have a system definition */
1805 }else {
1806 /* we have a plain value get the content remove next "
1807 and ' if defined */
1808 if (! ((axl_stream_inspect (stream, "\"", 1) > 0))) {
1809 if (! (axl_stream_inspect (stream, "\'", 1) > 0)) {
1810 axl_error_new (-2, "Expected to find entity value initiator (\") or ('), every entity value must start with them",
1811 stream, error);
1812 axl_stream_free (stream);
1813 return false;
1815 /* knowing that ' was matched, now get the attribute value */
1816 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, true, 1, "'");
1817 }else {
1818 /* knowhing that " was matched, now get the attribute value */
1819 string_aux = axl_stream_get_until (stream, NULL, &matched_chunk, true, 1, "\"");
1822 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "entity value found: [%s]", string_aux);
1824 /* nullify internal reference so we have the
1825 * only one reference to entity content value
1826 * inside string_aux */
1827 axl_stream_nullify (stream, LAST_CHUNK);
1829 /* set the value */
1830 entity->content = string_aux;
1833 /* consume previous white spaces */
1834 AXL_CONSUME_SPACES (stream);
1836 /* check last item to parse */
1837 if (! (axl_stream_inspect (stream, ">", 1) > 0)) {
1838 axl_error_new (-2, "Expected to find entity definition terminator (>), but it wasn't found",
1839 stream, error);
1840 axl_stream_free (stream);
1841 return false;
1844 return true;
1848 /**
1849 * @internal
1851 * Implements DTD parsing, reading it from a direct buffer, or a file
1852 * path or a file handle.
1854 axlDtd * __axl_dtd_parse_common (const char * entity, int entity_size,
1855 const char * file_path, int fd_handle,
1856 axlError ** error)
1858 axlStream * stream;
1859 axlDtd * dtd;
1860 int iterator;
1862 /* create the stream associated */
1863 stream = axl_stream_new (entity, entity_size, file_path, fd_handle, error);
1864 axl_return_val_if_fail (stream, NULL);
1866 dtd = __axl_dtd_new ();
1867 axl_stream_link (stream, dtd, (axlDestroyFunc) axl_dtd_free);
1869 iterator = 0;
1870 while (axl_stream_remains (stream)) {
1871 /* get rid from comments found */
1872 if (! axl_doc_consume_comments (NULL, stream, error))
1873 return NULL;
1875 /* check for element declaration */
1876 if (axl_stream_peek (stream, "<!ELEMENT", 9) > 0) {
1877 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD element declaration");
1878 /* found element declaration */
1879 if (! __axl_dtd_parse_element (dtd, stream, error))
1880 return NULL;
1882 continue;
1886 /* check for attribute list declarations */
1887 if (axl_stream_inspect (stream, "<!ATTLIST", 9) > 0) {
1888 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD attribute list declaration");
1890 /* parse it */
1891 if (! __axl_dtd_parse_attlist (dtd, stream, error))
1892 return NULL;
1894 continue;
1897 /* check for the entity declaration */
1898 if (axl_stream_peek (stream, "<!ENTITY", 8) > 0) {
1899 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD entity declaration");
1901 /* parse the entity definition */
1902 if (! __axl_dtd_parse_entity (dtd, stream, error))
1903 return NULL;
1905 continue;
1908 /* stop the loop */
1909 if (iterator == 3) {
1910 axl_error_new (-1, "unable to process DTD content, unable to found expected information", stream, error);
1911 axl_stream_free (stream);
1912 return NULL;
1914 iterator++;
1918 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD elements totally loaded, building references..");
1920 /* update current root reference, the DTD root for the DTD
1921 * document already parsed */
1922 if (dtd->elements != NULL)
1923 dtd->root = __axl_dtd_get_new_root (dtd);
1925 /* check if the DTD has ID declarations if found IDREF
1926 * declarations */
1927 if (! dtd->haveIdDecl && dtd->haveIdRefDecl) {
1928 axl_error_new (-1, "DTD semantic error, found IDREF attribute declaration but no attribute ID declaration was found.", stream, error);
1929 axl_stream_free (stream);
1930 return NULL;
1933 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD load COMPLETE");
1935 axl_stream_unlink (stream);
1936 axl_stream_free (stream);
1937 return dtd;
1940 /**
1941 * @brief Allows to parse the provided entity, which is expected to
1942 * contain a DTD (Document Type Definition).
1944 * @param entity The document type definition to parse.
1946 * @param entity_size The document size, or -1 to make the function to
1947 * figure out current size.
1949 * @param error An optional \ref axlError where errors will be reported.
1951 * @return A newly allocated \ref axlDtd that must be deallocated when
1952 * no longer need with \ref axl_dtd_free. The function could return
1953 * NULL on failure detected. On that case, it is requred to check \ref
1954 * axlError variable, if defined.
1956 axlDtd * axl_dtd_parse (const char * entity,
1957 int entity_size,
1958 axlError ** error)
1961 return __axl_dtd_parse_common (entity, entity_size, NULL, -1, error);
1964 /**
1965 * @brief Allows to parse the provided DTD definition, which is found
1966 * on the provided file path.
1968 * @param file_path The file path where it is expected to receive a
1969 * DTD file.
1971 * @param error An optional \ref axlError reference where all errors found will be reported.
1973 * @return A newly allocated \ref axlDtd instance or NULL if it fails.
1975 axlDtd * axl_dtd_parse_from_file (const char * file_path,
1976 axlError ** error)
1978 return __axl_dtd_parse_common (NULL, -1, file_path, -1, error);
1982 /**
1983 * @internal
1985 * Support function for axl_dtd_validate which checks if the provided
1986 * parent have its childs configuration according to the values
1987 * expresed on the sequenced represented by the itemList.
1989 * The function return true if the validation was ok, or false
1990 * if something have failed. It also creates an error, using the
1991 * optional axlError reference received.
1993 bool __axl_dtd_validate_sequence (axlNode * parent,
1994 int * child_position,
1995 axlDtdElementList * itemList,
1996 axlError ** error,
1997 bool try_match,
1998 bool top_level)
2000 int iterator = 0;
2001 int child_pos = *child_position;
2002 axlNode * node;
2003 axlDtdElementListNode * itemNode;
2004 bool status = false;
2005 bool one_matched;
2006 AxlDtdTimes times;
2009 axl_return_val_if_fail (parent, false);
2010 axl_return_val_if_fail (itemList, false);
2013 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validating a sequence list: iterator=%d, item list count=%d, at child position=%d",
2014 iterator, axl_dtd_item_list_count (itemList), child_pos);
2016 /* iterate over the sequence, checking its order */
2017 while (iterator < axl_dtd_item_list_count (itemList)) {
2019 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting next item node from the DTD item list at: %d",
2020 iterator);
2022 /* get the item node specification */
2023 itemNode = axl_dtd_item_list_get_node (itemList, iterator);
2024 one_matched = false;
2025 times = axl_dtd_item_node_get_repeat (itemNode);
2027 do {
2029 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "getting node at child position: %d",
2030 child_pos);
2032 /* get the node that is located at the same position
2033 * than the sequence */
2034 if (child_pos < axl_node_get_child_num (parent)) {
2035 node = axl_node_get_child_nth (parent, child_pos);
2036 } else
2037 node = NULL;
2039 /* the node child list have ended, check if
2040 * this situation was expected */
2041 if (node == NULL) {
2042 __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",
2043 child_pos, axl_node_get_name (parent), times,
2044 iterator, axl_dtd_item_list_count (itemList));
2045 /* check if we were working with a
2046 * list, which have matched at least
2047 * one item */
2048 if (times == ONE_OR_MANY && one_matched && status &&
2049 ((iterator + 1) == axl_dtd_item_list_count (itemList))) {
2050 *child_position = child_pos;
2051 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (III): %d", child_pos);
2052 return true;
2055 /* check that the reset of the
2056 * specification item is optional,
2057 * including the one used */
2058 status = true;
2059 do {
2060 if (times != ZERO_OR_MANY &&
2061 times != ZERO_OR_ONE) {
2063 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found item, inside the DTD item list, that is not optional: %d (repeat value: %d)",
2064 iterator, times);
2065 status = false;
2066 break;
2069 /* update index and get the next item */
2070 iterator++;
2071 if (iterator < axl_dtd_item_list_count (itemList))
2072 itemNode = axl_dtd_item_list_get_node (itemList, iterator);
2073 }while (status && (iterator < axl_dtd_item_list_count (itemList)));
2075 /* check status before checking the rest of the item spec */
2076 if (status) {
2077 *child_position = child_pos;
2079 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (II): %d", child_pos);
2081 return true;
2084 /* check if a try match is being runned */
2085 if (! try_match) {
2086 axl_error_new (-1, "Found that DTD specifies more nodes to be hold by the parent, but no more childs were found",
2087 NULL, error);
2090 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found that no nodes left to satisfy DTD validation operation");
2091 *child_position = child_pos;
2092 return false;
2095 /* check node type */
2096 if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_LIST) {
2098 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "the item node is an item list, dtd item list position: %d, child position: %d=<%s>",
2099 iterator, child_pos, axl_node_get_name (node));
2101 /* element list found, validate its content */
2102 if (! __axl_dtd_validate_item_list (axl_dtd_item_node_get_list (itemNode),
2103 parent, &child_pos, error, false)) {
2104 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sub item list validation have failed (not critical)");
2105 /* check if we are the top
2106 * level list and the itemNode
2107 * checked is the last one
2108 * item on the item list */
2109 if (top_level && ((iterator + 1) == axl_node_get_child_num (parent))) {
2110 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the last item list wasn't matched");
2114 *child_position = child_pos;
2115 return false;
2118 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validated item list, child position after: %d",
2119 child_pos);
2120 /* because child position updating and
2121 * repeat matching is already handled
2122 * by dtd_validate_item_list function
2123 * we just continue with the next
2124 * iteration */
2125 break;
2127 } else if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_NODE) {
2128 /* check the name against the spec */
2130 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG,
2131 "the item node is a final content particule definition: %s",
2132 axl_dtd_item_node_get_value (itemNode));
2134 status = NODE_CMP_NAME (node, axl_dtd_item_node_get_value (itemNode));
2137 /* check previous status */
2138 if ((times == ONE_AND_ONLY_ONE) ||
2139 (times == ONE_OR_MANY && one_matched == false)) {
2140 if (! status) {
2141 /* only report an upper level
2142 * error if we are not running
2143 * a try match */
2144 if (! try_match) {
2146 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL,
2147 "Found different node (<%s>) for a sequence expected (<%s>), at child position: %d, item list pos: %d",
2148 axl_node_get_name (node),
2149 axl_dtd_item_node_get_value (itemNode),
2150 child_pos, iterator);
2151 axl_error_new (-1, "Found a different node, inside a sequence, than the sequence especification (DTD)",
2152 NULL, error);
2154 /* return that a match wasn't possible */
2155 *child_position = child_pos;
2156 return false;
2160 /* according to the repetition pattern, update loop indexes */
2161 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "updating child nodes references: %d, repeat type: %d, status=%d",
2162 child_pos, times, status);
2165 /* one only item to match and exactly one */
2166 if (times == ONE_AND_ONLY_ONE) {
2167 child_pos++;
2168 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "updated child position to: %d, repeat type: %d, status=%d",
2169 child_pos, times, status);
2170 break;
2173 /* one or many items to match */
2174 if (times == ONE_OR_MANY) {
2176 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "matched one to many item node: status=%d one_matched=%d",
2177 status, one_matched);
2179 /* if the match have failed and
2180 * previous matches was ok, it seems
2181 * we have reached the next
2182 * items. Just break the loop */
2183 if (status == false && one_matched == true)
2184 break;
2186 child_pos++;
2187 one_matched = true;
2188 continue; /* don't break the loop */
2191 /* zero or optionally one item to match */
2192 if (times == ZERO_OR_ONE) {
2193 /* if previous status was ok, it seems
2194 * that we have matched the optional
2195 * character. In that case, move the
2196 * index to the following value. If
2197 * not, just break the loop. */
2198 if (status == true)
2199 child_pos++;
2200 break;
2203 /* zero or many items to match */
2204 if (times == ZERO_OR_MANY) {
2205 if (status == true) {
2206 one_matched = true;
2207 child_pos++;
2208 continue;
2210 break;
2214 /* until break the loop */
2215 }while (true);
2217 /* update iterator index */
2218 iterator++;
2221 /* check if more nodes where specified than the DTD spec */
2222 times = axl_dtd_item_list_repeat (itemList);
2223 if ((times == ONE_OR_MANY || times == ONE_AND_ONLY_ONE) &&
2224 top_level && (child_pos < axl_node_get_child_num (parent))) {
2226 /* drop a log */
2227 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> have more childs=%d than the childs iterated=%d, top_level=%d",
2228 axl_node_get_name (parent),
2229 axl_node_get_child_num (parent),
2230 child_pos, top_level);
2232 /* do not report an error found if a try match is
2233 * being run */
2234 if (! try_match) {
2235 axl_error_new (-1, "More childs, than the ones especified in the DTD, were found",
2236 NULL, error);
2238 /* return that the match wasn't possible */
2239 *child_position = child_pos;
2240 return false;
2243 /* return that the sequence has been validated */
2244 *child_position = child_pos;
2246 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence validated with child position (I): %d", child_pos);
2248 return true;
2251 /**
2252 * @internal
2254 * Internal support function to validate the choice list.
2256 bool __axl_dtd_validate_choice (axlNode * parent, int * child_position,
2257 axlDtdElementList * itemList,
2258 axlError ** error,
2259 bool try_match, bool top_level)
2261 axlNode * node;
2262 axlDtdElementListNode * itemNode;
2263 int iterator;
2264 bool status;
2265 AxlDtdTimes times;
2266 bool one_match;
2269 if (*child_position < axl_node_get_child_num (parent)) {
2270 /* get a reference to be matched by the choice list */
2271 node = axl_node_get_child_nth (parent, *child_position);
2272 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validated choice list at position: %d=<%s>", *child_position, axl_node_get_name (node));
2273 } else {
2274 /* tried to match a choice list with a child index
2275 * outside the maximum number of childs */
2276 if (! try_match) {
2277 axl_error_new (-1, "Unable to match choice list, it seems that the are not enough childs to validate the choice list",
2278 NULL, error);
2280 return false;
2283 iterator = 0;
2284 while (iterator < axl_dtd_item_list_count (itemList)) {
2285 /* get the DTD item list to match */
2286 itemNode = axl_dtd_item_list_get_node (itemList, iterator);
2287 times = axl_dtd_item_node_get_repeat (itemNode);
2289 if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_NODE) {
2290 /* reset match configuration */
2291 one_match = false;
2292 repeat_for_node:
2293 /* a node was found */
2294 status = NODE_CMP_NAME (node, axl_dtd_item_node_get_value (itemNode));
2296 /* know, if the node was matched check it
2297 * repetition configuration */
2298 if (status) {
2299 /* update child position */
2300 (*child_position)++;
2302 if (times == ONE_AND_ONLY_ONE || times == ZERO_OR_ONE) {
2303 /* the node was matched and
2304 * the itemNode has a one and
2305 * only one configuration,
2306 * just return that the choice
2307 * list was matched */
2308 return true;
2310 if (times == ONE_OR_MANY || times == ZERO_OR_MANY) {
2311 /* because the node was matched, but the repetition
2312 * pattern allows to match more nodes we have to
2313 * iterate a bit more */
2314 node = axl_node_get_child_nth (parent, *child_position);
2315 if (node == NULL) {
2316 /* because we already matched at least one item,
2317 * we can assume that the itemNode was successfully
2318 * matched for both cases (*) and (+). */
2319 return true;
2321 /* flag the one match */
2322 one_match = true;
2324 /* if the node reference is
2325 * not NULL, try to match the
2326 * next item */
2327 goto repeat_for_node;
2329 } /* end if */
2331 /* before returning, that that we have matched
2332 * previously, at least, one node for
2333 * one-to-many and zero-to-many pattern */
2334 if ((times == ONE_OR_MANY || times == ZERO_OR_MANY) && one_match) {
2335 return true;
2338 } else if (axl_dtd_item_node_get_type (itemNode) == AXL_ELEMENT_LIST) {
2339 /* an element list was found, call to validate it */
2340 /* element list found, validate its content */
2341 if (__axl_dtd_validate_item_list (axl_dtd_item_node_get_list (itemNode),
2342 parent, child_position, error, false)) {
2343 /* item list matched */
2344 return true;
2348 /* no item was matched, update iterator indexes */
2349 iterator++;
2352 /* seems that the choice list wasn't matched */
2353 if (! try_match) {
2354 axl_error_new (-1, "Unable to match choice list, after checking all posibilities, choice list wasn't validated",
2355 NULL, error);
2357 return false;
2360 /**
2361 * @internal
2363 * Tries to perform a validation, based on the item list received and
2364 * the repetition configuration.
2366 * @param itemList The item list containing DTD content spec
2367 * information used to validate.
2369 * @param parent The parent node where the validation process is being
2370 * applied. The content spec refers to the childs the parent has.
2372 * @param stack An stack used by the overall process to store the
2373 * subsequent parents to be validated. This stack must be released if
2374 * a error is found.
2376 * @param error An optional axlError reference containing the error
2377 * textual diagnostic if found.
2379 * @return true if the validation was ok, otherwise false is
2380 * returned.
2382 bool __axl_dtd_validate_item_list (axlDtdElementList * itemList,
2383 axlNode * parent,
2384 int * child_position,
2385 axlError ** error,
2386 bool top_level)
2388 int temp_child_pos;
2389 int caller_child_pos;
2390 bool status;
2391 bool already_matched;
2394 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "validating an item list with repeat pattern: %d, at %d, top level=%d",
2395 axl_dtd_item_list_repeat (itemList), *child_position, top_level);
2397 /* store current caller child position value to check it before */
2398 caller_child_pos = *child_position;
2400 /* now check repetition type */
2401 switch (axl_dtd_item_list_repeat (itemList)) {
2402 case ONE_AND_ONLY_ONE:
2403 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (one and only one) spec..");
2404 if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2406 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a SEQUENCE form");
2407 /* it is a choice, so the item list specifies
2408 * the nodes that could appear */
2409 if (!__axl_dtd_validate_sequence (parent, child_position, itemList, error,
2410 false, top_level)) {
2411 return false;
2413 }else {
2414 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a CHOICE form");
2415 /* it is a sequence, so, item list
2416 * specification represents the nodes, in the
2417 * order they must appear */
2418 if (!__axl_dtd_validate_choice (parent, child_position, itemList, error,
2419 false, top_level)) {
2420 return false;
2422 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "choice list was properly validated");
2424 break;
2425 case ZERO_OR_ONE:
2427 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or one) spec..");
2428 if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2430 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "using a SEQUENCE form, parent: <%s>",
2431 axl_node_get_name (parent));
2432 /* because we are running a zero or one item
2433 * list matching, we don't care if it doesn't
2434 * match. In the case it match, the child
2435 * position is updated and next calls will be
2436 * properly aligned. In the match doesn't
2437 * happens, it also don't matter because the
2438 * pattern allow to not match */
2439 temp_child_pos = *child_position;
2440 if (!__axl_dtd_validate_sequence (parent, child_position, itemList, error,
2441 true, top_level)) {
2442 /* check that the match wasn't
2443 * produced, at any level */
2444 if (temp_child_pos != *child_position) {
2445 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch (%d != %d)",
2446 temp_child_pos, *child_position);
2447 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",
2448 NULL, error);
2449 return false;
2452 return false;
2455 }else {
2456 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
2458 /* it is a sequence, so, item list
2459 * specification represents the nodes, in the
2460 * order they must appear */
2461 __axl_dtd_validate_choice (parent, child_position, itemList, error,
2462 true, top_level);
2464 break;
2465 case ZERO_OR_MANY:
2467 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or many) spec..");
2468 if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2470 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a SEQUENCE (size: %d) form ",
2471 axl_dtd_item_list_count (itemList));
2472 /* one this case, several matches must be
2473 * tried, until the validation fails */
2474 do {
2475 temp_child_pos = *child_position;
2476 status = __axl_dtd_validate_sequence (parent, child_position, itemList, error,
2477 true, top_level);
2479 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence match status=%d", status);
2480 if (! status) {
2481 /* check that the match wasn't
2482 * produced, at any level */
2483 if ((temp_child_pos != *child_position)) {
2485 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch (%d != %d)",
2486 temp_child_pos, *child_position);
2487 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",
2488 NULL, error);
2489 return false;
2492 }while (status);
2493 }else {
2495 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
2496 /* it is a sequence, so, item list
2497 * specification represents the nodes, in the
2498 * order they must appear */
2499 do {
2500 status = __axl_dtd_validate_choice (parent, child_position, itemList, error,
2501 true, top_level);
2502 }while (status);
2504 break;
2505 case ONE_OR_MANY:
2506 /* one or many sequence spec (+) */
2507 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found a (zero or many) spec..");
2508 if (axl_dtd_item_list_type (itemList) == SEQUENCE) {
2510 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a SEQUENCE (size: %d) form ",
2511 axl_dtd_item_list_count (itemList));
2513 /* one this case, several matches must be
2514 * tried, until the validation fails */
2515 already_matched = false;
2516 do {
2517 temp_child_pos = *child_position;
2518 /* try to match the one or many
2519 sequence according to the value
2520 stored inside already matched */
2521 status = __axl_dtd_validate_sequence (parent, child_position, itemList, error,
2522 already_matched, top_level);
2524 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "sequence match status=%d", status);
2525 if (! status) {
2526 /* check that the match wasn't
2527 * produced, at any level */
2528 if ((temp_child_pos != *child_position)) {
2530 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "item list mismatch, matched partially (%d != %d)",
2531 temp_child_pos, *child_position);
2532 axl_error_new (-1,
2533 "Found an DTD item list definition, that should be matched entirely or not, one or many times, but it was matched partially",
2534 NULL, error);
2535 return false;
2537 }else {
2538 /* set that we have matched, at least, one item */
2539 already_matched = true;
2544 }while (status);
2545 }else {
2547 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " using a CHOICE form");
2548 /* it is a sequence, so, item list
2549 * specification represents the nodes, in the
2550 * order they must appear */
2551 already_matched = false;
2552 do {
2553 /* the next choice matching is done
2554 * according to the value stored in
2555 * already matched */
2556 status = __axl_dtd_validate_choice (parent, child_position, itemList, error,
2557 already_matched, top_level);
2558 /* if the validation successed, set
2559 * that next matched are not required
2560 * to be successful ones */
2561 if (status)
2562 already_matched = true;
2563 }while (status);
2565 break;
2566 default:
2567 /* this case will never be reached */
2568 #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."
2569 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, INTERNAL_ERROR_01);
2570 axl_error_new (-1, INTERNAL_ERROR_01, NULL, error);
2571 return false;
2575 __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",
2576 top_level, axl_dtd_item_list_type (itemList), *child_position, axl_node_get_child_num (parent));
2578 /* check that, in the case that the choice item list is being
2579 * validated, ensure it has validated all nodes, especially if
2580 * we are the top level definition */
2581 if (top_level && (axl_dtd_item_list_type (itemList) == CHOICE)) {
2582 if (((*child_position) + 1) < axl_node_get_child_num (parent)) {
2585 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the choice list didn't cover all childs (%d), while parent=<%s> has: (%d)",
2586 (*child_position), axl_node_get_name (parent), axl_node_get_child_num (parent));
2587 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",
2588 NULL, error);
2589 return false;
2593 /* element type children validated */
2594 return true;
2597 /**
2598 * @internal
2600 * Support function validate parent nodes which are element type
2601 * children ones.
2603 bool __axl_dtd_validate_element_type_children (axlDtdElement * element,
2604 axlNode * parent,
2605 bool top_level,
2606 axlError ** error)
2608 axlDtdElementList * itemList;
2609 int child_pos = 0;
2610 char * err_msg;
2612 /* get a reference to the item list */
2613 itemList = axl_dtd_get_item_list (element);
2615 /* check for xml nodes with fewer content than the initially
2616 * expected. */
2617 if (axl_node_get_child_num (parent) < element->minimum_match) {
2618 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.",
2619 axl_node_get_name (parent),
2620 axl_node_get_child_num (parent),
2621 element->minimum_match);
2622 axl_error_new (-1, err_msg, NULL, error);
2623 axl_free (err_msg);
2624 return false;
2627 /* validate the item list, starting from the child 0 */
2628 if (__axl_dtd_validate_item_list (itemList, parent, &child_pos, error, top_level)) {
2629 /* check if, at least, all minimum elements was
2630 * matched */
2631 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking minimum node match: (%d < min: %d) (%d < childs: %d) node=<%s>",
2632 child_pos, element->minimum_match,
2633 child_pos, axl_node_get_child_num (parent),
2634 axl_node_get_name (parent));
2636 if (child_pos < axl_node_get_child_num (parent)) {
2637 axl_error_new (-1, "Found that the validation process didn't cover all nodes. All xml child nodes inside the parent wasn't covered. This means that the xml document have more content than the DTD spec.",
2638 NULL, error);
2639 return false;
2641 /* seems that the minimum match */
2642 return true;
2645 return false;
2648 /**
2649 * @internal
2650 * Internal support function to validate #PCDATA nodes.
2652 bool __axl_dtd_validate_element_type_pcdata (axlDtdElement * element,
2653 axlNode * parent,
2654 axlStack * stack,
2655 axlError ** error)
2657 /* check for childs */
2658 if (axl_node_have_childs (parent)) {
2659 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "node <%s> should be #PCDATA and it contains childs",
2660 axl_node_get_name (parent));
2661 axl_error_new (-1,
2662 "Found a node for which its espeficiation makes it to be a node with only data and no childs, and it currently contains childs",
2663 NULL, error);
2664 return false;
2667 /* return that the validation was ok */
2668 return true;
2671 /**
2672 * @internal
2674 * Support function to validate empty nodes.
2676 bool __axl_dtd_validate_element_type_empty (axlDtdElement * element,
2677 axlNode * parent,
2678 axlStack * stack,
2679 axlError ** error)
2681 /* check the node is indeed, empty */
2682 if (! axl_node_is_empty (parent)) {
2683 axl_error_new (-1, "Found a node that it is especified that must be empty, but it isn't",
2684 NULL, error);
2685 return false;
2688 /* check the node doesn't have childs */
2689 if (axl_node_have_childs (parent)) {
2690 axl_error_new (-1, "Found a node that it is especified that must be empty, but it has childs",
2691 NULL, error);
2692 return false;
2695 /* return that the validation was ok */
2696 return true;
2699 bool __axl_dtd_attr_validate_foreach (const char * key, const char * value, axlPointer data, axlPointer data2)
2701 axlDtdAttribute * attribute = data;
2702 axlError ** error = data2;
2703 axlDtdAttributeDecl * decl;
2704 char * err_msg;
2706 /* get declaration associated */
2707 decl = axl_list_lookup (attribute->list, __find_attr_decl, (axlPointer) key);
2709 if (decl == NULL) {
2710 /* found an error */
2711 err_msg = axl_strdup_printf ("Found an attribute (%s) which is not specified by the attribute declaration for <%s>",
2712 key, attribute->name);
2713 axl_error_new (-1, err_msg, NULL, error);
2715 /* free the cursor and the error message */
2716 axl_free (err_msg);
2718 /* return true here because we want to stop the process */
2719 return true;
2721 } /* end if */
2723 /* if the declaration is found, now check its
2724 * contraints */
2725 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking contraint for attribute: %s=%s", decl->name, value);
2726 if (decl->type == CDATA_ATTRIBUTE) {
2727 /* found free attribute declaration, go ahed */
2728 } else if (decl->type == ENUMERATION_TYPE) {
2729 /* found an enumeration type, check and return */
2730 if (axl_list_lookup (decl->enumvalues, axl_list_find_string, (char *) value) == NULL) {
2731 /* found an error */
2732 err_msg = axl_strdup_printf ("Found an attribute (%s) with a value not allowed by the enum declaration (%s) for the node <%s>",
2733 key, value, attribute->name);
2734 axl_error_new (-1, err_msg, NULL, error);
2736 /* free the cursor and the error message */
2737 axl_free (err_msg);
2738 return true;
2740 } else {
2741 /* not supported yet */
2744 /* return false to continue with the process */
2745 return false;
2748 bool __axl_dtd_attr_validate_required (axlPointer element, axlPointer data)
2750 axlNode * node = data;
2751 axlDtdAttributeDecl * decl = element;
2753 switch (decl->defaults) {
2754 case ATT_REQUIRED:
2755 /* attribute required */
2756 return !HAS_ATTR (node, decl->name);
2757 case ATT_FIXED:
2758 return !HAS_ATTR_VALUE (node, decl->name, decl->default_value);
2759 default:
2760 break;
2761 } /* end switch */
2763 /* return false for this because it is not obligatory
2764 * to have the attribute defined. */
2765 return false;
2768 /**
2769 * @internal Functions which validates the attribute declaration for
2770 * the node provided, using attribute declarations found.
2772 * @param node The node to check for its attributes.
2774 * @param dtd The dtd used to validate the node provided.
2776 * @param error A reference to the axlError where the textual
2777 * diagnostic error will be reported.
2779 * @return true if the node is validated, false if not.
2781 bool axl_dtd_attr_validate (axlNode * node, axlDtd * dtd, axlError ** error, axlHash * id_validation, axlList * idref_validation)
2783 axlDtdAttribute * attribute;
2784 axlDtdAttributeDecl * decl;
2785 char * err_msg;
2786 int iterator;
2787 axlError * _error = NULL;
2789 /* find attribute contraints for the node */
2790 attribute = axl_dtd_get_attr (dtd, axl_node_get_name (node));
2791 if (attribute == NULL)
2792 return true;
2794 /* we have an especification, run it */
2796 /* for each attribute found, check against the spec */
2797 axl_node_attr_foreach (node, __axl_dtd_attr_validate_foreach, attribute, &_error);
2799 /* check the error */
2800 if (! axl_error_was_ok (_error)) {
2801 /* reconfigure error returned */
2802 if (error != NULL)
2803 *error = _error;
2804 return false;
2805 } /* end if */
2808 /* now, for each contraint, check that all required nodes
2809 * exists */
2810 decl = axl_list_lookup (attribute->list, __axl_dtd_attr_validate_required, node);
2811 if (decl != NULL) {
2812 if (decl->defaults == ATT_FIXED)
2813 err_msg = axl_strdup_printf ("attribute required '%s' (or its value), due to #FIXED declaration, not found for node <%s>",
2814 decl->name, attribute->name);
2815 else
2816 err_msg = axl_strdup_printf ("attribute required '%s', due to #REQUIRED declaration, not found for node <%s>",
2817 decl->name, attribute->name);
2818 axl_error_new (-1, err_msg, NULL, error);
2819 axl_free (err_msg);
2820 return false;
2821 } /* end if */
2823 /* check declarations */
2824 if (dtd->haveIdDecl) {
2826 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found DTD has ID unique attribute declaration..");
2828 /* check if the node have TOKENIZED_TYPE_ID */
2829 decl = axl_list_lookup (attribute->list, __find_id_decl, NULL);
2831 /* if we have a tokenized */
2832 if (decl != NULL) {
2834 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "found ID unique attribute declaration %s=\"%s\"..",
2835 decl->name, ATTR_VALUE (node, decl->name));
2837 /* check if the attribute value for the decl that is
2838 * flagged as ID is already found at the
2839 * id_validation */
2840 if (axl_hash_exists (id_validation, (axlPointer) ATTR_VALUE (node, decl->name))) {
2841 err_msg = axl_strdup_printf ("DTD declared the attribute '%s' as unique (ID) for the node %s, but was found used several times",
2842 decl->name, attribute->name);
2843 axl_error_new (-1, err_msg, NULL, error);
2844 axl_free (err_msg);
2845 return false;
2846 } /* end if */
2848 /* seems the attribute was not used, nice!, store it */
2849 axl_hash_insert (id_validation, (axlPointer) ATTR_VALUE (node, decl->name), (axlPointer) ATTR_VALUE (node, decl->name));
2850 } /* end if */
2851 } /* end if */
2853 if (dtd->haveIdRefDecl) {
2854 /* find the id ref declaration */
2856 iterator = 0;
2857 while (iterator < axl_list_length (attribute->list)) {
2859 /* get the attribute declaration at the
2860 * particular position */
2861 decl = axl_list_get_nth (attribute->list, iterator);
2862 if (decl->type == TOKENIZED_TYPE_IDREF) {
2863 /* found a reference, but do not check
2864 * it at this place becase the
2865 * reference could be placed at any
2866 * part in the document event after
2867 * the reference pointed is
2868 * defined. store and check later */
2869 if (ATTR_VALUE (node, decl->name)) {
2870 /* store the id ref reference
2871 * if defined */
2872 axl_list_add (idref_validation, (axlPointer) ATTR_VALUE (node, decl->name));
2874 } /* end if */
2876 /* get the next */
2877 iterator++;
2879 } /* end if */
2881 } /* end if */
2883 axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "attributes validated for node=<%s>", attribute->name);
2885 return true;
2888 /**
2889 * @internal Function used by axl_dtd_validate_references to ensure
2890 * that all references found point to a valid reference defined.
2892 bool __axl_dtd_reference_check (axlPointer _element, axlPointer data)
2894 #if defined(SHOW_DEBUG_LOG)
2895 const char * value = _element;
2897 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking id ref: %s", value);
2898 #endif
2900 return ! axl_hash_exists ((axlHash *) data, _element);
2903 /**
2904 * @internal Function that validates all references found (from IDREF
2905 * attribute) to unique references (defined by ID attributes).
2908 bool axl_dtd_validate_references (axlHash * id_validation, axlList * idref_validation, axlError ** error)
2910 char * reference;
2911 char * err_msg;
2913 /* if no empty at the valiadtion reference list, means not
2914 * reference was done, so there is no room for errors */
2915 if (idref_validation == NULL)
2916 return true;
2918 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "id_validation reference: 0x%x", id_validation);
2920 /* find first reference not found */
2921 reference = axl_list_lookup (idref_validation, __axl_dtd_reference_check, id_validation);
2923 if (reference != NULL) {
2924 /* found a reference not defined, report it to the
2925 * application level */
2926 err_msg = axl_strdup_printf ("Found a reference defined ('%s') which is not found in any ID attribute in the document",
2927 reference);
2928 axl_error_new (-1, err_msg, NULL, error);
2929 axl_free (err_msg);
2931 return false;
2932 } /* end if */
2934 /* validation ok */
2935 return true;
2938 /**
2939 * @brief Allows to validate the given XML document (\ref axlDoc)
2940 * against the given document type definition (DTD, \ref axlDtd).
2942 * This function allows to validate your XML documents providing the
2943 * document type definition, that was read using \ref axl_dtd_parse or
2944 * \ref axl_dtd_parse_from_file.
2946 * Keep in mind that a document could be well-formed and valid. The
2947 * only difference is that valid XML document are those that, meet all
2948 * XML rules, but also are clasified and recognized as XML documents
2949 * with some particular structure, that is represented (or
2950 * constrained) with providing a DTD definition.
2952 * @param doc The \ref axlDoc containing the XML document to be
2953 * validated.
2955 * @param dtd The \ref axlDtd containing the DTD definition used to
2956 * validate the document.
2958 * @param error An optional reference to a \ref axlError object where
2959 * validation errors are reported.
2961 * @return true if the document is valid, false if not.
2963 bool axl_dtd_validate (axlDoc * doc, axlDtd * dtd,
2964 axlError ** error)
2966 axlNode * parent;
2967 axlStack * stack;
2968 axlHash * id_validation = NULL;
2969 axlList * idref_validation = NULL;
2971 axlDtdElement * element;
2972 bool top_level;
2973 char * err_msg;
2974 bool result;
2976 /* perform some checkings */
2977 axl_return_val_if_fail (doc, false);
2978 axl_return_val_if_fail (dtd, false);
2980 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "starting DTD validation");
2982 /* validate the very first root node */
2983 parent = axl_doc_get_root (doc);
2984 element = axl_dtd_get_root (dtd);
2985 if ((element != NULL) && ! NODE_CMP_NAME (parent, axl_dtd_get_element_name (element))) {
2987 /* because a DTD document could have several top level
2988 * elements, ensure this is not the case */
2989 element = axl_dtd_get_element (dtd, axl_node_get_name (parent));
2990 if (element == NULL) { /* || ! axl_dtd_element_is_toplevel (dtd, element)) { */
2991 /* root node doesn't match */
2992 axl_error_new (-1, "Found that root node doesn't match!", NULL, error);
2993 return false;
2995 } /* end if */
2996 } /* end if */
2998 /* check if the node has DTD element declaration */
2999 if (element == NULL) {
3000 err_msg = axl_strdup_printf ("There is not DTD element declaration to validate the node <%s>",
3001 axl_node_get_name (parent));
3002 axl_error_new (-1, err_msg, NULL, error);
3003 axl_free (err_msg);
3004 return false;
3005 } /* end if */
3007 /* check if the dtd contains a Id declaration */
3008 if (dtd->haveIdDecl) {
3009 /* seems the user have declarted ID attributes init the hash */
3010 id_validation = axl_hash_new (axl_hash_string, axl_hash_equal_string);
3011 } /* end if */
3013 /* check if the dtd contains Id ref declarations */
3014 if (dtd->haveIdRefDecl) {
3015 /* create a list that could contain all references done */
3016 idref_validation = axl_list_new (axl_list_always_return_1, NULL);
3017 } /* end if */
3019 /* check empty content spec */
3020 if (axl_dtd_get_element_type (element) == ELEMENT_TYPE_EMPTY) {
3021 /* check if the document provided have only one node */
3022 result = axl_node_is_empty (parent) && !axl_node_have_childs (parent) && axl_dtd_attr_validate (parent, dtd, error, id_validation, idref_validation);
3024 /* check references */
3025 if (result)
3026 result = axl_dtd_validate_references (id_validation, idref_validation, error);
3028 /* free and return */
3029 axl_hash_free (id_validation);
3031 /* free the list */
3032 axl_list_free (idref_validation);
3033 return result;
3034 } /* end if */
3036 /* queue initial nodes to validate */
3037 stack = axl_stack_new (NULL);
3040 /* set that the only top level node is the first one */
3041 top_level = true;
3043 do {
3044 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "doing a DTD iteration: <%s>...",
3045 axl_node_get_name (parent));
3047 /* validate attributes */
3048 if (! axl_dtd_attr_validate (parent, dtd, error, id_validation, idref_validation)) {
3049 /* free the stack */
3050 axl_stack_free (stack);
3052 /* free id_validation */
3053 axl_hash_free (id_validation);
3055 /* free the list */
3056 axl_list_free (idref_validation);
3057 return false;
3060 /* reach this position, the <parent> reference contains
3061 * a reference to the parent node, which will be used
3062 * to validate current child content against current
3063 * configuration for dtd element constraining it.
3065 * equally, the <element> reference contains a dtd
3066 * reference to the already checked DTD element which
3067 * configure this parent node. */
3068 switch (axl_dtd_get_element_type (element)) {
3069 case ELEMENT_TYPE_PCDATA:
3070 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find PCDATA dtd element=%s: parent=<%s>, ",
3071 axl_dtd_get_element_name (element),
3072 axl_node_get_name (parent));
3074 /* ok, a leaf node was found, know it is
3075 * required to check that the node doesn't
3076 * have more childs and only have content,
3077 * that is, it is not empty */
3078 if (!__axl_dtd_validate_element_type_pcdata (element, parent, stack, error)) {
3079 /* free id_validation */
3080 axl_hash_free (id_validation);
3082 /* free the stack */
3083 axl_stack_free (stack);
3085 /* free the list */
3086 axl_list_free (idref_validation);
3087 return false;
3089 break;
3090 case ELEMENT_TYPE_CHILDREN:
3092 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find CHILDREN dtd element");
3093 /* ok, a parent node that have childs */
3094 if (!__axl_dtd_validate_element_type_children (element, parent, top_level, error)) {
3095 /* free id_validation */
3096 axl_hash_free (id_validation);
3098 /* free the stack */
3099 axl_stack_free (stack);
3101 /* free the list */
3102 axl_list_free (idref_validation);
3104 return false;
3106 break;
3107 case ELEMENT_TYPE_EMPTY:
3108 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find EMPTY dtd element");
3109 /* the element especification is empty, the
3110 * node being validated must also be the
3111 * same */
3112 if (!__axl_dtd_validate_element_type_empty (element, parent, stack, error)) {
3113 /* free id_validation */
3114 axl_hash_free (id_validation);
3116 /* free the stack */
3117 axl_stack_free (stack);
3119 /* free the list */
3120 axl_list_free (idref_validation);
3122 return false;
3124 break;
3125 case ELEMENT_TYPE_ANY:
3126 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find ANY dtd element");
3127 /* the anything is allowed cased from this
3128 * parent node. */
3129 goto continue_with_validation;
3130 case ELEMENT_TYPE_MIXED:
3131 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, " find MIXED dtd element");
3133 /* the mixed case, where nodes and PC data
3134 * could be mixed */
3135 break;
3136 default:
3137 /* do not do any thing on this case */
3138 break;
3141 /* queue more childs, as future parents to be
3142 * validated on the provided queue, only in the case
3143 * the parent node have childs */
3144 if (axl_node_have_childs (parent)) {
3146 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> have childs, adding its childs (stack size: %d)",
3147 axl_node_get_name (parent),
3148 axl_stack_size (stack));
3150 /* queue childs to be processed */
3151 __axl_dtd_queue_childs (stack, parent);
3153 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "parent node <%s> childs: %d, (stack size: %d)",
3154 axl_node_get_name (parent), axl_node_get_child_num (parent),
3155 axl_stack_size (stack));
3158 /* set the parent reference to NULL */
3159 parent = NULL;
3161 /* update the reference to the new parent node, only
3162 * if there are new parents on the stack */
3163 continue_with_validation:
3164 if (! axl_stack_is_empty (stack)) {
3167 /* get a new reference */
3168 parent = axl_stack_pop (stack);
3170 /* get a reference to the DTD element to used */
3171 element = axl_dtd_get_element (dtd, axl_node_get_name (parent));
3172 if (element == NULL) {
3173 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "found that the node <%s> doesn't have DTD especification",
3174 axl_node_get_name (parent));
3175 /* prepare the error message */
3176 err_msg = axl_strdup_printf ("Found a node <%s> that doesn't have a DTD element espefication to validate it, DTD validation failed",
3177 axl_node_get_name (parent));
3178 axl_error_new (-1, err_msg, NULL, error);
3179 axl_free (err_msg);
3181 /* free id_validation */
3182 axl_hash_free (id_validation);
3184 /* free the list */
3185 axl_list_free (idref_validation);
3187 /* free the stack */
3188 axl_stack_free (stack);
3189 return false;
3190 } /* end if */
3191 } /* end if */
3193 /* set the top level status */
3194 top_level = false;
3196 /* until the stack is empty */
3197 }while (parent != NULL);
3199 /* check references */
3200 result = axl_dtd_validate_references (id_validation, idref_validation, error);
3202 /* deallocate stack used */
3203 axl_stack_free (stack);
3205 /* free id_validation */
3206 axl_hash_free (id_validation);
3208 /* free the list */
3209 axl_list_free (idref_validation);
3211 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "DTD validation, %s", result ? "ok" : "failed");
3213 /* the document is valid */
3214 return result;
3217 /**
3218 * @brief Allows to get the root node for the provided DTD.
3220 * Every DTD have a root node defined, which is the root node accepted
3221 * for the set of XML document considered to be valid under the
3222 * definition of the DTD provided.
3224 * The value returned is the name of the root node that must have the
3225 * XML document being validated.
3227 * @param dtd The \ref axlDtd where the root node name will be
3228 * returned.
3230 * @return A reference to the internal representation of the root node
3231 * Value must not be deallocated.
3233 axlDtdElement * axl_dtd_get_root (axlDtd * dtd)
3235 axl_return_val_if_fail (dtd, NULL);
3237 /* return current status for the root node */
3238 if (dtd->root == NULL) {
3239 __axl_log (LOG_DOMAIN, AXL_LEVEL_CRITICAL, "dtd root element not defined");
3240 return NULL;
3242 return dtd->root;
3245 /**
3246 * @internal function used by \ref axl_dtd_get_element to perform node
3247 * lookups.
3249 bool __find_dtd_element (axlPointer _element, axlPointer data)
3251 axlDtdElement * element = _element;
3252 char * name = data;
3254 /* check the name */
3255 if (axl_cmp (element->name, name))
3256 return true;
3258 /* it is not the element */
3259 return false;
3262 /**
3263 * @brief Allows to get the DTD element (\ref axlDtdElement), inside
3264 * the provided DTD (\ref axlDtd), that represent the spefication for
3265 * the node called by the provided name.
3267 * @param dtd The DTD (\ref axlDtd) where the lookup will be
3268 * performed.
3270 * @param name The element name to lookup.
3272 * @return A reference to the \ref axlDtdElement searched or NULL if
3273 * fails. The function also returns NULL if values received are NULL.
3275 axlDtdElement * axl_dtd_get_element (axlDtd * dtd, const char * name)
3278 axl_return_val_if_fail (dtd, NULL);
3279 axl_return_val_if_fail (name, NULL);
3281 /* perform the lookup */
3282 return axl_list_lookup (dtd->elements, __find_dtd_element, (axlPointer) name);
3285 /**
3286 * @internal function used by \ref axl_dtd_get_attr to perform node
3287 * lookups.
3289 bool __find_dtd_attr (axlPointer _element, axlPointer data)
3291 axlDtdAttribute * attr = _element;
3292 char * name = data;
3294 /* check the name */
3295 if (axl_cmp (attr->name, name))
3296 return true;
3298 /* it is not the element */
3299 return false;
3302 /**
3303 * @brief Allows to get the set of attribute declerations for a
3304 * particular node.
3306 * The \ref axlDtdAttribute declaration contains all constraints
3307 * configured for attributes found for the particular xml node
3308 * (identified by <b>name</b>).
3310 * @param dtd A reference to the DTD document.
3312 * @param nodeName The xml node that is requested to return all attribute
3313 * declarations.
3315 * @return A reference to the \ref axlDtdAttribute or NULL if it
3316 * fails.
3318 axlDtdAttribute * axl_dtd_get_attr (axlDtd * dtd,
3319 const char * nodeName)
3321 axl_return_val_if_fail (dtd, NULL);
3322 axl_return_val_if_fail (nodeName, NULL);
3324 /* perform the lookup */
3325 return axl_list_lookup (dtd->attributes, __find_dtd_attr, (axlPointer) nodeName);
3328 /**
3329 * @brief Allows to get the number of constraints that have been
3330 * configured for the particular node.
3332 * @param dtd The reference to the DTD document.
3334 * @param nodeName The name of the node that is being asked for its
3335 * constraints.
3337 * @return 0 or the number of contraints. The function return -1 if
3338 * any of the parameter received is null.
3340 int axl_dtd_get_attr_contraints (axlDtd * dtd,
3341 const char * nodeName)
3343 axlDtdAttribute * attr;
3345 axl_return_val_if_fail (dtd, -1);
3346 axl_return_val_if_fail (nodeName, -1);
3348 /* get the attribute specification for the node */
3349 attr = axl_dtd_get_attr (dtd, nodeName);
3351 /* return the number of items */
3352 return axl_list_length (attr->list);
3355 /**
3356 * @brief Returns the name of the provided \ref axlDtdElement.
3358 * @param element A reference to a \ref axlDtdElement where the name
3359 * will be returned.
3361 * @return A reference to the internal DTD element name. Returned
3362 * value mustn't be deallocated.
3364 char * axl_dtd_get_element_name (axlDtdElement * element)
3366 axl_return_val_if_fail (element, NULL);
3368 return element->name;
3371 /**
3372 * @brief Returns current element type for the provided \ref axlDtdElement.
3374 * @param element The axlDtdElement where its type will be returned.
3376 * @return Current element type for the provided node.
3378 AxlDtdElementType axl_dtd_get_element_type (axlDtdElement * element)
3380 axl_return_val_if_fail (element, ELEMENT_TYPE_UNKNOWN);
3382 return element->type;
3385 /**
3386 * @brief Returns current DTD content specification, represented by the Item list.
3388 * @param element The DTD element (\ref axlDtdElement) which is being
3389 * requested to return its \ref axlDtdElementList.
3391 * @return The \ref axlDtdElementList reference. The value returned
3392 * must not be deallocated. The function returns NULL if the reference received is NULL.
3394 axlDtdElementList * axl_dtd_get_item_list (axlDtdElement * element)
3396 axl_return_val_if_fail (element, NULL);
3398 return element->list;
3401 /**
3402 * @brief Allows to check if the provided DTD ELEMENT representation
3403 * is a top level definition.
3405 * @param dtd The DTD document where the operation will be performed.
3406 * @param element The \ref axlDtdElement to check.
3408 * @return \ref true if the dtd element is a top level element or
3409 * \ref false if not. The function returns \ref false if the
3410 * provided reference is NULL.
3412 bool axl_dtd_element_is_toplevel (axlDtd * dtd, axlDtdElement * element)
3414 /* support several top level definitions */
3415 int iterator;
3416 axlDtdElement * dtd_element_aux;
3418 axl_return_val_if_fail (dtd, false);
3419 axl_return_val_if_fail (element, false);
3421 /* check which is the top */
3422 iterator = 0;
3423 while (iterator < axl_list_length (dtd->elements)) {
3425 /* get the next reference */
3426 dtd_element_aux = axl_list_get_nth (dtd->elements, iterator);
3428 /* check which is the top */
3429 if (__axl_dtd_get_is_parent (dtd_element_aux, element)) {
3430 /* the element provided have a parent */
3431 return false;
3434 /* update inner loop iterator */
3435 iterator ++;
3436 } /* while end */
3438 /* return that the provided node doesn't have a parent node */
3439 return true;
3442 /**
3443 * @brief Returns the number of item nodes (\ref
3444 * axlDtdElementListNode) inside the item list received (\ref axlDtdElementList).
3446 * @param itemList The \ref axlDtdElementList where the count
3447 * operation is being requested.
3449 * @return The number of item list the provided \ref axlDtdElementList
3450 * reference has. The function return -1 if the provided reference is
3451 * NULL.
3453 int axl_dtd_item_list_count (axlDtdElementList * itemList)
3455 axl_return_val_if_fail (itemList, -1);
3457 if (itemList->itemList == NULL)
3458 return 0;
3460 return axl_list_length (itemList->itemList);
3463 /**
3464 * @brief Allows to get current configuration for the provided item
3465 * list, which is the content specification for a DTD element.
3467 * @param itemList The item list where the operation will be
3468 * performed.
3470 * @return Current configuration (\ref SEQUENCE or a \ref CHOICE).
3472 AxlDtdNestedType axl_dtd_item_list_type (axlDtdElementList * itemList)
3474 axl_return_val_if_fail (itemList, -1);
3476 return itemList->type;
3479 /**
3480 * @brief Allows to get current configuration for DTD content spec
3481 * repetition.
3483 * @param itemList The content spec where the query will be performed.
3485 * @return Current configuration for times to be repeated DTD element
3486 * content specification.
3488 AxlDtdTimes axl_dtd_item_list_repeat (axlDtdElementList * itemList)
3490 axl_return_val_if_fail (itemList, DTD_TIMES_UNKNOWN);
3492 /* returns current times configuration */
3493 return itemList->times;
3496 /**
3497 * @brief Allows to get the provided item node reference (\ref
3498 * axlDtdElementListNode) from the provided item list (\ref
3499 * axlDtdElementList).
3501 * Provided position ranges from 0 up to \ref axl_dtd_item_list_count.
3503 * @param itemList The itemList where the operation will be performed.
3504 * @param position The position where the item node will be looked up.
3506 * @return A reference to the \ref axlDtdElementListNode, or NULL if
3507 * there is no item node at the selected index. The function return
3508 * NULL if the provided position is a non positive value or it is
3509 * greater than the current item list count (\ref
3510 * axl_dtd_item_list_count) or the provided item list reference is
3511 * NULL.
3513 axlDtdElementListNode * axl_dtd_item_list_get_node (axlDtdElementList * itemList,
3514 int position)
3516 axl_return_val_if_fail (itemList, NULL);
3517 axl_return_val_if_fail (position >= 0, NULL);
3518 axl_return_val_if_fail (position < axl_dtd_item_list_count (itemList), NULL);
3520 return axl_list_get_nth (itemList->itemList, position);
3523 /**
3524 * @brief Allows to get current node type for the provided DTD element
3525 * type content particule or item node (\ref axlDtdElementListNode).
3527 * @param node The node where the type is being requested.
3529 * @return It returns if the item node contains a final leaf node,
3530 * making a reference to an explicit node naming that is allowed to be
3531 * used in the context where is found the provided \ref
3532 * axlDtdElementListNode or a \ref axlDtdElementList containing more
3533 * nodes or lists.
3535 NodeType axl_dtd_item_node_get_type (axlDtdElementListNode * node)
3537 axl_return_val_if_fail (node, AXL_ELEMENT_NOT_DEFINED);
3538 return node->type;
3541 /**
3542 * @brief Returns the item list inside the provided node.
3544 * The node is supported to contain an item list reference or NULL
3545 * will be returned. Check \ref axl_dtd_item_node_get_type.
3547 * @param node The node where the operation will be performed.
3549 * @return The item list inside the node or NULL if fails.
3551 axlDtdElementList * axl_dtd_item_node_get_list (axlDtdElementListNode * node)
3553 axl_return_val_if_fail (node, NULL);
3554 axl_return_val_if_fail (node->type == AXL_ELEMENT_LIST, NULL);
3556 return node->data;
3559 /**
3560 * @brief Allows to get the dtd item list value, which represents the
3561 * node name that is being constrained/represented.
3563 * @param node The item node where the value is being requested.
3565 * @return The value inside the item node, supposing it contains an
3566 * leaf item node or NULL if fails. The value returned must not be
3567 * deallocated.
3569 char * axl_dtd_item_node_get_value (axlDtdElementListNode * node)
3571 axl_return_val_if_fail (node, NULL);
3572 if (node->type != AXL_ELEMENT_NODE)
3573 return "requested-value-on-a-list";
3575 return node->data;
3578 /**
3579 * @brief Allows to get current configuration for the provided content
3580 * particule for the times to be repeated.
3582 * @param node The content particule where the query will be
3583 * performed.
3585 * @return Return current repetition configuration.
3587 AxlDtdTimes axl_dtd_item_node_get_repeat (axlDtdElementListNode * node)
3589 axlDtdElementList * list;
3591 axl_return_val_if_fail (node, DTD_TIMES_UNKNOWN);
3594 if (node->type == AXL_ELEMENT_NODE) {
3595 /* return value requested */
3596 return node->times;
3599 if (node->type == AXL_ELEMENT_LIST) {
3600 /* return the requested value for an item list */
3601 list = node->data;
3602 return list->times;
3605 /* return that we don't know man */
3606 return DTD_TIMES_UNKNOWN;
3610 /**
3611 * @internal
3613 * Internal function which allows to lookup the DTD entity reference
3614 * provided the name and the type.
3616 axlDtdEntity * __axl_dtd_entity_lookup (axlDtd * dtd,
3617 const char * name,
3618 axlDtdEntityType type)
3620 axlDtdEntity * entity;
3621 int iterator;
3622 int length;
3624 /* check values received */
3625 axl_return_val_if_fail (dtd, NULL);
3626 axl_return_val_if_fail (name, NULL);
3628 /* lookup for the item */
3629 iterator = 0;
3630 length = axl_list_length (dtd->entities);
3631 while (iterator < length) {
3633 /* get the entity at the provided position */
3634 entity = axl_list_get_nth (dtd->entities, iterator);
3636 /* check the type and the name */
3637 if ((entity->type == type) && axl_cmp (entity->name, name))
3638 return entity;
3640 /* update iterator */
3641 iterator++;
3642 } /* end while */
3644 return NULL;
3647 /**
3648 * @brief Allows to check if the provided entity name, with the
3649 * provided type is defined on the given DTD object.
3651 * @param dtd The \ref axlDtd instance where the entity lookup will be
3652 * performed.
3654 * @param name The entity name to lookup.
3656 * @param type The entity type to lookup.
3658 * @return true if an entity is found named as provided with the type
3659 * provided. Othewise, false is returned.
3661 bool axl_dtd_entity_exists (axlDtd * dtd,
3662 const char * name,
3663 axlDtdEntityType type)
3665 /* return if the entity exists */
3666 return (__axl_dtd_entity_lookup (dtd, name, type) != NULL);
3669 /**
3670 * @brief Allows to get the content configured inside the entity that
3671 * is identified by the provided name and the provided type.
3673 * @param dtd The DTD where the lookup will be performed.
3675 * @param name The entity name to lookup for its content.
3677 * @param type The entity type to match.
3679 * @return An internal reference to the content associated to the
3680 * entity found or NULL. In case the content is defined (as return
3681 * value) it must not be deallocated.
3683 char * axl_dtd_entity_value (axlDtd * dtd,
3684 const char * name,
3685 axlDtdEntityType type)
3687 axlDtdEntity * entity;
3689 /* get the entity reference */
3690 entity = __axl_dtd_entity_lookup (dtd, name, type);
3692 /* check the entity reference */
3693 axl_return_val_if_fail (entity, NULL);
3695 /* return the content */
3696 return entity->content;
3699 /**
3700 * @brief Allows to destroy the provided \ref axlDtd document.
3702 * @param dtd The \ref axlDtd document to destroy.
3704 void axl_dtd_free (axlDtd * dtd)
3706 if (dtd == NULL) {
3707 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "received a null DTD reference, doing nothing");
3708 return;
3711 __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "releasing the DTD reference");
3712 /* free dtd elements */
3713 if (dtd->elements)
3714 axl_list_free (dtd->elements);
3716 /* free entities */
3717 if (dtd->entities)
3718 axl_list_free (dtd->entities);
3720 /* free attributes */
3721 if (dtd->attributes)
3722 axl_list_free (dtd->attributes);
3724 /* free the node itself */
3725 axl_free (dtd);
3727 return;
3731 /**
3732 * @internal
3734 * @brief Allows to release the memory hold by the given
3735 * axlDtdElement.
3737 * @param element The axlDtdElement to release.
3739 void axl_dtd_element_free (axlDtdElement * element)
3741 if (element == NULL)
3742 return;
3744 /* free element name */
3745 if (element->name != NULL)
3746 axl_free (element->name);
3748 /* free element list definitions */
3749 axl_dtd_item_list_free (element->list);
3751 /* free element itself */
3752 axl_free (element);
3754 return;
3757 /**
3758 * @internal
3760 * @brief Deallocates memory used by the \ref axlDtdElementList
3761 * reference.
3763 * @param list The reference to deallocate.
3765 void axl_dtd_item_list_free (axlDtdElementList * list)
3767 if (list == NULL)
3768 return;
3770 /* check and deallocate the list provided */
3771 if (list->itemList != NULL)
3772 axl_list_free (list->itemList);
3774 /* deallocates the node itself */
3775 axl_free (list);
3776 return;
3779 /* @} */