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