1 /* @source ajdom **************************************************************
2 **
3 ** AJAX Document Object Model (DOM) functions
4 **
5 ** @author Copyright (C) 2006 Alan Bleasby
6 ** @version $Revision: 1.38 $
7 ** @modified Jul 03 2006 ajb First version
8 ** @modified $Date: 2012/11/14 14:19:11 $ by $Author: rice $
9 ** @@
10 **
11 ** This library is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU Lesser General Public
13 ** License as published by the Free Software Foundation; either
14 ** version 2.1 of the License, or (at your option) any later version.
15 **
16 ** This library is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ** Lesser General Public License for more details.
20 **
21 ** You should have received a copy of the GNU Lesser General Public
22 ** License along with this library; if not, write to the Free Software
23 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 ** MA  02110-1301,  USA.
25 **
26 ******************************************************************************/
27 
28 /* ========================================================================= */
29 /* ============================= include files ============================= */
30 /* ========================================================================= */
31 
32 #include "ajlib.h"
33 #include "ajdom.h"
34 #include "ajfileio.h"
35 #include "expat.h"
36 
37 #include <string.h>
38 
39 
40 
41 
42 /* ========================================================================= */
43 /* =============================== constants =============================== */
44 /* ========================================================================= */
45 
46 
47 
48 
49 /* ========================================================================= */
50 /* =========================== global variables ============================ */
51 /* ========================================================================= */
52 
53 
54 
55 
56 /* ========================================================================= */
57 /* ============================= private data ============================== */
58 /* ========================================================================= */
59 
60 
61 
62 
63 /* ========================================================================= */
64 /* =========================== private constants =========================== */
65 /* ========================================================================= */
66 
67 /* @conststatic domKCmtx ******************************************************
68 **
69 ** AJAX DOM cmtx
70 **
71 ******************************************************************************/
72 
73 static const ajuint domKCmtx[] =
74 {
75     0x00dd, /* element    */
76     0x0014, /* attribute  */
77     0x0000, /* text       */
78     0x0000, /* cdata      */
79     0x00dd, /* entity ref */
80     0x00dd, /* entity     */
81     0x0000, /* proc inst  */
82     0x0000, /* comment    */
83     0x02c1, /* doc        */
84     0x0820, /* doc type   */
85     0x00dd, /* doc frag   */
86     0x0000  /* notation   */
87 };
88 
89 
90 
91 
92 /* @conststatic domKNodeinfo **************************************************
93 **
94 ** AJAX DOM Node Type names
95 **
96 ******************************************************************************/
97 
98 static const char *domKNodeinfo[] =
99 {
100     "ajEDomNodeTypeNULL",
101     "ajEDomNodeTypeElement",
102     "ajEDomNodeTypeAttribute",
103     "ajEDomNodeTypeText",
104     "ajEDomNodeTypeCdataSection",
105     "ajEDomNodeTypeEntityReference",
106     "ajEDomNodeTypeEntityNode",
107     "ajEDomNodeTypeProcessingInstruction",
108     "ajEDomNodeTypeComment",
109     "ajEDomNodeTypeDocumentNode",
110     "ajEDomNodeTypeDocumentType",
111     "ajEDomNodeTypeDocumentFragment",
112     "ajEDomNodeTypeNotation",
113     (const char *) NULL
114 };
115 
116 
117 
118 
119 #define AJDOM_TABLE_HINT 1000
120 
121 #define AJXML_BUFSIZ 8192
122 
123 #define AJDOM_NOCHILD(p,q) !(domKCmtx[(p)->type - 1] & (1 << ((q)->type - 1)))
124 #define AJDOM_DOCMOD(p,q) ((p)->type == ajEDomNodeTypeDocumentNode &&   \
125                            (q)->type == ajEDomNodeTypeElement)
126 #define AJDOM_DOCTYPEMOD(p,q) ((p)->type == ajEDomNodeTypeDocumentNode && \
127                                (q)->type == ajEDomNodeTypeDocumentType)
128 #define AJDOM_CANTDO(p,q) (AJDOM_NOCHILD(p,q) ||                \
129                            (AJDOM_DOCMOD(p,q) &&                \
130                             (p)->sub.Document.documentelement))
131 
132 
133 
134 
135 /* ========================================================================= */
136 /* =========================== private variables =========================== */
137 /* ========================================================================= */
138 
139 
140 
141 
142 /* ========================================================================= */
143 /* =========================== private functions =========================== */
144 /* ========================================================================= */
145 
146 static AjBool           domIsAncestor(const AjPDomNode node,
147                                       const AjPDomNode parent);
148 
149 static AjPDomNodeEntry  domDoLookupNode(const AjPDomNodeList list,
150                                         const AjPDomNode node);
151 static void             domUpdateNode(AjPDomNode node);
152 static AjPDomNode       domDoRemoveChild(AjPDomNode node, AjPDomNode child);
153 static void             domRemoveFromMap(AjPDomNodeList list,
154                                          const AjPDomNode key);
155 static void             domAddToMap(AjPDomNodeList list, AjPDomNode key,
156                                     AjPDomNodeEntry val);
157 
158 
159 static AjPDomNode       domNodeListItemFiltered(const AjPDomNodeList list,
160                                                 ajint indx, ajuint nodetype);
161 
162 static void             domTraverse(AjPDomNodeList list, AjPDomNode node,
163                                     const AjPStr tagname);
164 static void             domTraverseC(AjPDomNodeList list, AjPDomNode node,
165                                      const char *tagname);
166 
167 static AjPDomNode       domNodeCloneNode(AjPDomDocument ownerdocument,
168                                          const AjPDomNode node, AjBool deep);
169 
170 
171 static void             domWriteEncoded(const AjPStr s, AjPFile outf);
172 static void             domWriteEncodedIndent(const AjPStr s, AjPFile outf,
173                                               ajuint indent);
174 
175 static AjPDomUserdata   domUserdataNew(void);
176 static void domUserdataDel(AjPDomUserdata *thys);
177 
178 static void domExpatStart(void *udata, const XML_Char *name,
179                           const XML_Char **atts);
180 static void domExpatEnd(void *udata, const XML_Char *name);
181 static void domExpatChardata(void *udata, const XML_Char *str,
182                              int len);
183 static void domExpatCdataStart(void *udata);
184 static void domExpatCdataEnd(void *udata);
185 static void domExpatComment(void *udata, const XML_Char *str);
186 static void domExpatXmlDecl(void *udata, const XML_Char *version,
187                             const XML_Char *encoding, int standalone);
188 static void domExpatDoctypeStart(void *udata, const XML_Char *doctypename,
189                                  const XML_Char *sysid, const XML_Char *pubid,
190                                  int hasinternalsubset);
191 static void domExpatDoctypeEnd(void *udata);
192 static void domExpatElement(void *udata, const XML_Char *name,
193                             XML_Content *model);
194 static void domExpatAttlist(void *udata, const XML_Char *name,
195                             const XML_Char *attname, const XML_Char *atttype,
196                             const XML_Char *deflt, int isrequired);
197 static void domExpatEntity(void *udata,
198                            const XML_Char *entityname, int isparam,
199                            const XML_Char *value, int lenval,
200                            const XML_Char *base, const XML_Char *systemid,
201                            const XML_Char *publicid, const XML_Char *notname);
202 static void domExpatNotation(void *udata, const XML_Char *notname,
203                              const XML_Char *base, const XML_Char *systemid,
204                              const XML_Char *publicid);
205 static void domClearMapValues(void **key, void **value, void *cl);
206 static void domClearMapAll(void **key, void **value, void *cl);
207 
208 
209 
210 
211 /* ========================================================================= */
212 /* ======================= All functions by section ======================== */
213 /* ========================================================================= */
214 
215 
216 
217 
218 /* @funcstatic domRemoveFromMap ***********************************************
219 **
220 ** Remove a key/value pair from a DOM node list
221 **
222 ** @param [w] list [AjPDomNodeList] node list
223 ** @param [r] key [const AjPDomNode] key
224 ** @return [void]
225 **
226 ** @release 4.0.0
227 ** @@
228 ******************************************************************************/
229 
domRemoveFromMap(AjPDomNodeList list,const AjPDomNode key)230 static void domRemoveFromMap(AjPDomNodeList list, const AjPDomNode key)
231 {
232     AjPDomNode trukey;
233 
234     if(!list->table)
235         return;
236 
237     /*
238     ** don't free the values, just remove them from the map
239     ** if(val)
240     */
241 
242     ajTableRemoveKey(list->table, key, (void **) &trukey);
243 
244     return;
245 }
246 
247 
248 
249 
250 /* @funcstatic domAddToMap ****************************************************
251 **
252 ** Add a key/value pair to a DOM node list
253 **
254 ** @param [w] list [AjPDomNodeList] node list
255 ** @param [o] key [AjPDomNode] key
256 ** @param [u] val [AjPDomNodeEntry] value
257 ** @return [void]
258 **
259 ** @release 4.0.0
260 ** @@
261 ******************************************************************************/
262 
domAddToMap(AjPDomNodeList list,AjPDomNode key,AjPDomNodeEntry val)263 static void domAddToMap(AjPDomNodeList list, AjPDomNode key,
264                         AjPDomNodeEntry val)
265 {
266     if(!list->table)
267         list->table = ajTableNew(AJDOM_TABLE_HINT);
268 
269 
270     domRemoveFromMap(list, key);
271 
272     ajTablePut(list->table, key, val);
273 
274     return;
275 }
276 
277 
278 
279 
280 /* @func ajDomNodeListGetLen **************************************************
281 **
282 ** Return the length of a DOM node list
283 **
284 ** @param [r] list [const AjPDomNodeList] node list
285 ** @return [ajint] Length (-1 if error)
286 **
287 ** @release 6.3.0
288 ** @@
289 ******************************************************************************/
290 
ajDomNodeListGetLen(const AjPDomNodeList list)291 ajint ajDomNodeListGetLen(const AjPDomNodeList list)
292 {
293 
294     if(!list)
295         return -1;
296 
297     return list->length;
298 }
299 
300 
301 
302 
303 /* @func ajDomNodeListAppend **************************************************
304 **
305 ** Append a child node to a DOM node list
306 **
307 ** @param [w] list [AjPDomNodeList] node list
308 ** @param [u] child [AjPDomNode] child
309 ** @return [AjPDomNodeEntry] Node list child entry created
310 **
311 ** @release 4.0.0
312 ** @@
313 ******************************************************************************/
314 
ajDomNodeListAppend(AjPDomNodeList list,AjPDomNode child)315 AjPDomNodeEntry ajDomNodeListAppend(AjPDomNodeList list,
316                                     AjPDomNode child)
317 {
318     AjPDomNodeEntry p;
319     AjPDomDocumentType doctype;
320 
321     if(!list)
322     {
323         ajDebug("ajDomNodeListAppend: Null pointer error\n");
324         return NULL;
325     }
326 
327     if(list->filter)
328     {
329         ajDebug("ajDomNodeListAppend: Filtered list error\n");
330         return NULL;
331     }
332 
333     AJNEW0(p);
334 
335     domAddToMap(list, child, p);
336 
337     p->node = child;
338 
339     if(!list->first)
340     {
341         list->first = p;
342         list->last  = p;
343     }
344     else
345     {
346         list->last->next = p;
347         p->prev = list->last;
348         list->last = p;
349     }
350 
351     ++list->length;
352 
353     if(child->ownerdocument &&
354        (doctype = child->ownerdocument->sub.Document.doctype) &&
355        list == doctype->childnodes)
356     {
357         if(child->type == ajEDomNodeTypeNotation)
358             ++doctype->sub.DocumentType.notations->length;
359         else if(child->type == ajEDomNodeTypeEntityNode)
360             ++doctype->sub.DocumentType.entities->length;
361     }
362 
363     if(child->type == ajEDomNodeTypeAttribute)
364         child->sub.Attr.ownerelement = list->ownerelement;
365 
366     return p;
367 }
368 
369 
370 
371 
372 /* @func ajDomNodeAppendChild *************************************************
373 **
374 ** Appends an extra child at the end of the child nodes list of a node.
375 ** If extrachild is already in the list, it is first removed.
376 **
377 ** @param [w] node [AjPDomNode] node
378 ** @param [u] extrachild [AjPDomNode] extra child
379 ** @return [AjPDomNode] Pointer to extrachild or NULL if error
380 **
381 ** @release 4.0.0
382 ** @@
383 ******************************************************************************/
384 
ajDomNodeAppendChild(AjPDomNode node,AjPDomNode extrachild)385 AjPDomNode ajDomNodeAppendChild(AjPDomNode node, AjPDomNode extrachild)
386 {
387     AjPDomNode n    = NULL;
388     AjPDomNode next = NULL;
389 
390     if(!node || !extrachild)
391         return NULL;
392 
393     if(extrachild->ownerdocument != node->ownerdocument &&
394        node->type != ajEDomNodeTypeDocumentNode &&
395        extrachild->type != ajEDomNodeTypeDocumentType)
396     {
397         ajDebug("ajDomNodeAppendChild: Wrong document");
398         return NULL;
399     }
400 
401     if(extrachild->type == ajEDomNodeTypeDocumentFragment)
402     {
403         for(n = extrachild->firstchild; n; n = n->nextsibling)
404             if(AJDOM_CANTDO(node, n) || domIsAncestor(n, node))
405             {
406                 ajDebug("ajDomNodeAppendChild: Hierarchy Request Error\n");
407                 return NULL;
408             }
409 
410         for(n = extrachild->firstchild; n; n = next)
411         {
412             next = n->nextsibling;
413 
414             if(!ajDomRemoveChild(extrachild, n))
415                 return NULL;
416 
417             if(!ajDomNodeAppendChild(node, n))
418             {
419                 ajDomDocumentDestroyNode(n->ownerdocument, &n);
420                 return NULL;
421             }
422         }
423 
424         return extrachild;
425     }
426 
427 
428     if(AJDOM_CANTDO(node, extrachild) || domIsAncestor(extrachild, node))
429     {
430         ajDebug("ajDomNodeAppendChild: Hierarchy Request Error 2\n");
431         return NULL;
432     }
433 
434     domDoRemoveChild(node, extrachild);
435 
436     if(!ajDomNodeListAppend(node->childnodes, extrachild))
437         return NULL;
438 
439     if(!node->firstchild)
440     {
441         node->firstchild = extrachild;
442         node->lastchild  = extrachild;
443         extrachild->previoussibling = NULL;
444         extrachild->nextsibling = NULL;
445     }
446     else
447     {
448         node->lastchild->nextsibling = extrachild;
449         extrachild->previoussibling = node->lastchild;
450         node->lastchild = extrachild;
451     }
452 
453     extrachild->nextsibling = NULL;
454     extrachild->parentnode = node;
455 
456     if(AJDOM_DOCMOD(node, extrachild))
457         node->sub.Document.documentelement = extrachild;
458     else if(AJDOM_DOCTYPEMOD(node, extrachild))
459     {
460         node->sub.Document.doctype = extrachild;
461         extrachild->ownerdocument = node;
462     }
463 
464     domUpdateNode(node);
465 
466     return extrachild;
467 }
468 
469 
470 
471 
472 /* @funcstatic domUpdateNode **************************************************
473 **
474 ** Update the common parent entry of a node
475 **
476 ** @param [w] node [AjPDomNode] node
477 ** @return [void]
478 **
479 ** @release 4.0.0
480 ** @@
481 ******************************************************************************/
482 
domUpdateNode(AjPDomNode node)483 static void domUpdateNode(AjPDomNode node)
484 {
485     AjPDomNode p = NULL;
486     AjPDomNode n = NULL;
487 
488     if(!node || !node->ownerdocument)
489         return;
490 
491     if(!node->ownerdocument->sub.Document.commonparent)
492     {
493         node->ownerdocument->sub.Document.commonparent = node;
494         return;
495     }
496 
497     p = NULL;
498 
499     for(n = node; n; n = n->parentnode)
500         if(n == node->ownerdocument->sub.Document.commonparent)
501             return;
502         else if(!p && n->subtreeModified == 1)
503             p = n;
504         else
505             n->subtreeModified = 1;
506 
507     node->ownerdocument->sub.Document.commonparent = p;
508 
509     return;
510 }
511 
512 
513 
514 
515 /* @funcstatic domIsAncestor **************************************************
516 **
517 ** Tests whether a putative parent is the parent of a child
518 **
519 ** @param [r] node [const AjPDomNode] node
520 ** @param [r] parent [const AjPDomNode] putative parent
521 ** @return [AjBool] true if ancestor found
522 **
523 ** @release 4.0.0
524 ** @@
525 ******************************************************************************/
526 
domIsAncestor(const AjPDomNode node,const AjPDomNode parent)527 static AjBool domIsAncestor(const AjPDomNode node, const AjPDomNode parent)
528 {
529     const AjPDomNode p = NULL;
530 
531     for(p = parent; p; p = p->parentnode)
532         if(p == node)
533             return ajTrue;
534 
535     return ajFalse;
536 }
537 
538 
539 
540 
541 /* @func ajDomRemoveChild *****************************************************
542 **
543 ** Removes a child node from a list of children
544 **
545 ** @param [w] node [AjPDomNode] node
546 ** @param [u] child [AjPDomNode] child to remove
547 ** @return [AjPDomNode] child removed
548 **
549 ** @release 4.0.0
550 ** @@
551 ******************************************************************************/
552 
ajDomRemoveChild(AjPDomNode node,AjPDomNode child)553 AjPDomNode ajDomRemoveChild(AjPDomNode node, AjPDomNode child)
554 {
555     if(!node || !child)
556         return NULL;
557 
558     if(child->ownerdocument != node->ownerdocument &&
559        child->ownerdocument != node)
560     {
561         ajDebug("ajDomRemoveChild: Wrong document");
562 
563         return NULL;
564     }
565 
566     child = domDoRemoveChild(node, child);
567 
568     return child;
569 }
570 
571 
572 
573 
574 /* @func ajDomNodeListExists **************************************************
575 **
576 ** Check whether a child exists in a node list
577 **
578 ** @param [u] list [AjPDomNodeList] list
579 ** @param [r] child [const AjPDomNode] child
580 ** @return [AjBool] true if child is in the list
581 **
582 ** @release 4.0.0
583 ** @@
584 ******************************************************************************/
585 
ajDomNodeListExists(AjPDomNodeList list,const AjPDomNode child)586 AjBool ajDomNodeListExists(AjPDomNodeList list, const AjPDomNode child)
587 {
588     AjPDomNodeEntry e = NULL;
589 
590     if(!list || list->filter)
591         return ajFalse;
592 
593     e = domDoLookupNode(list, child);
594 
595     if(e)
596         return ajTrue;
597 
598     return ajFalse;
599 }
600 
601 
602 
603 
604 /* @funcstatic domDoLookupNode ************************************************
605 **
606 ** Return a pointer to a given node of a node list
607 **
608 ** @param [r] list [const AjPDomNodeList] list
609 ** @param [r] node [const AjPDomNode] node
610 ** @return [AjPDomNodeEntry] node entry or NULL if not found
611 **
612 ** @release 4.0.0
613 ** @@
614 ******************************************************************************/
615 
domDoLookupNode(const AjPDomNodeList list,const AjPDomNode node)616 static AjPDomNodeEntry domDoLookupNode(const AjPDomNodeList list,
617                                        const AjPDomNode node)
618 {
619     AjPDomNodeEntry p;
620 
621     p = (AjPDomNodeEntry) ajTableFetchmodV(list->table, node);
622 
623     return p;
624 }
625 
626 
627 
628 
629 /* @func ajDomNodeListRemove **************************************************
630 **
631 ** Remove a child from a node list
632 **
633 ** @param [w] list [AjPDomNodeList] list
634 ** @param [u] child [AjPDomNode] child
635 ** @return [AjPDomNodeEntry] child removed or NULL if not found
636 **
637 ** @release 4.0.0
638 ** @@
639 ******************************************************************************/
640 
ajDomNodeListRemove(AjPDomNodeList list,AjPDomNode child)641 AjPDomNodeEntry ajDomNodeListRemove(AjPDomNodeList list, AjPDomNode child)
642 {
643     AjPDomNodeEntry e;
644     AjPDomNode trukey;
645 
646     if(!list)
647     {
648         ajDebug("ajDomNodeListRemove: Empty list");
649         return NULL;
650     }
651 
652     if(list->filter)
653     {
654         ajDebug("ajDomNodeListRemove: Filtered list error");
655         return NULL;
656     }
657 
658     e = domDoLookupNode(list, child);
659 
660     if(!e)
661         return NULL;
662 
663     /*
664     ** Don't delete the key/value - just remove them from the lookup
665     ** table. The value is still required
666     */
667 
668     ajTableRemoveKey(list->table, child, (void **) &trukey);
669 
670     if(list->first == list->last)
671     {
672         list->first = NULL;
673         list->last  = NULL;
674     }
675     else if(e == list->first)
676     {
677         list->first = e->next;
678         list->first->prev = NULL;
679     }
680     else if(e == list->last)
681     {
682         list->last = e->prev;
683         list->last->next = NULL;
684     }
685     else
686     {
687         e->prev->next = e->next;
688         e->next->prev = e->prev;
689     }
690 
691     --list->length;
692 
693     if(child->type == ajEDomNodeTypeAttribute)
694         child->sub.Attr.ownerelement = NULL;
695 
696     return e;
697 }
698 
699 
700 
701 
702 /* @funcstatic domDoRemoveChild ***********************************************
703 **
704 ** Low level removal of a child node from a list of children
705 **
706 ** @param [w] node [AjPDomNode] node
707 ** @param [u] child [AjPDomNode] child to remove
708 ** @return [AjPDomNode] child removed
709 **
710 ** @release 4.0.0
711 ** @@
712 ******************************************************************************/
713 
domDoRemoveChild(AjPDomNode node,AjPDomNode child)714 static AjPDomNode domDoRemoveChild(AjPDomNode node, AjPDomNode child)
715 {
716     AjPDomNodeEntry e = NULL;
717 
718     if(!ajDomNodeListExists(node->childnodes, child))
719         return NULL;
720 
721     e = ajDomNodeListRemove(node->childnodes, child);
722     free(e);
723 
724     if(node->firstchild == node->lastchild)
725     {
726         node->firstchild = NULL;
727         node->lastchild = NULL;
728     }
729     else if(child == node->firstchild)
730     {
731         node->firstchild = child->nextsibling;
732         node->firstchild->previoussibling = NULL;
733     }
734     else if(child == node->lastchild)
735     {
736         node->lastchild = child->previoussibling;
737         node->lastchild->nextsibling = NULL;
738     }
739     else
740     {
741         child->previoussibling->nextsibling = child->nextsibling;
742         child->nextsibling->previoussibling = child->previoussibling;
743     }
744 
745     child->previoussibling = NULL;
746     child->nextsibling = NULL;
747     child->parentnode = NULL;
748 
749     if(AJDOM_DOCMOD(node, child))
750         node->sub.Document.documentelement = NULL;
751     else if(AJDOM_DOCTYPEMOD(node, child))
752     {
753         node->sub.Document.doctype = NULL;
754         child->ownerdocument = NULL;
755     }
756     else
757         domUpdateNode(node);
758 
759     return child;
760 }
761 
762 
763 
764 
765 /* @func ajDomDocumentDestroyNode *********************************************
766 **
767 ** Frees a node and its children
768 **
769 ** @param [w] doc [AjPDomDocument] document
770 ** @param [d] Pnode [AjPDomNode*] node
771 ** @return [void]
772 **
773 ** @release 4.0.0
774 ** @@
775 ******************************************************************************/
776 
ajDomDocumentDestroyNode(AjPDomDocument doc,AjPDomNode * Pnode)777 void ajDomDocumentDestroyNode(AjPDomDocument doc, AjPDomNode *Pnode)
778 {
779     AjPDomNode node;
780 
781     if(!Pnode)
782         return;
783 
784     if(!*Pnode)
785         return;
786 
787     node = *Pnode;
788 
789     if(node->childnodes)
790         ajDomDocumentDestroyNodeList(doc, &node->childnodes, AJDOMDESTROY);
791 
792     ajDebug("ajDomDocumentDestroyNode type '%u'", node->type);
793 
794     switch(node->type)
795     {
796         case ajEDomNodeTypeElement:
797             ajDomDocumentDestroyNodeList(doc, &node->attributes, AJDOMDESTROY);
798             ajStrDel(&node->name);
799             break;
800         case ajEDomNodeTypeText:
801             ajStrDel(&node->name);
802             ajStrDel(&node->value);
803             break;
804         case ajEDomNodeTypeComment:
805             ajStrDel(&node->name);
806             ajStrDel(&node->value);
807             break;
808         case ajEDomNodeTypeCdataSection:
809             ajStrDel(&node->name);
810             ajStrDel(&node->value);
811             break;
812         case ajEDomNodeTypeAttribute:
813             ajStrDel(&node->name);
814             ajStrDel(&node->value);
815             break;
816         case ajEDomNodeTypeEntityReference:
817         case ajEDomNodeTypeEntityNode:
818             ajStrDel(&node->name);
819             ajStrDel(&node->value);
820             ajStrDel(&node->sub.Entity.publicid);
821             ajStrDel(&node->sub.Entity.systemid);
822             ajStrDel(&node->sub.Entity.notationname);
823             break;
824         case ajEDomNodeTypeProcessingInstruction:
825             ajStrDel(&node->name);
826             ajStrDel(&node->value);
827             break;
828         case ajEDomNodeTypeDocumentNode:
829             ajStrDel(&node->name);
830             ajStrDel(&node->sub.Document.version);
831             ajStrDel(&node->sub.Document.encoding);
832             break;
833         case ajEDomNodeTypeDocumentType:
834             ajDomDocumentDestroyNodeList(doc, &node->sub.DocumentType.entities,
835                                          AJDOMDESTROY);
836             ajDomDocumentDestroyNodeList(doc, &node->sub.DocumentType.notations,
837                                          AJDOMDESTROY);
838             ajStrDel(&node->sub.DocumentType.publicid);
839             ajStrDel(&node->sub.DocumentType.systemid);
840             ajStrDel(&node->name);
841             break;
842         case ajEDomNodeTypeNotation:
843             ajStrDel(&node->sub.Notation.publicid);
844             ajStrDel(&node->sub.Notation.systemid);
845             ajStrDel(&node->name);
846             break;
847         default:
848             ajDebug("ajDomDocumentDestroyNode got unexpected "
849                     "AJAX DOM Node type %d.\n", node->type);
850     }
851 
852     AJFREE(*Pnode);
853 
854     return;
855 }
856 
857 
858 
859 
860 /* @func ajDomDocumentDestroyNodeList *****************************************
861 **
862 ** Frees a node list
863 **
864 ** @param [w] doc [AjPDomDocument] document
865 ** @param [d] Plist [AjPDomNodeList*] list
866 ** @param [r] donodes [AjBool] free nodes as well if true
867 ** @return [void]
868 **
869 ** @release 4.0.0
870 ** @@
871 ******************************************************************************/
872 
ajDomDocumentDestroyNodeList(AjPDomDocument doc,AjPDomNodeList * Plist,AjBool donodes)873 void ajDomDocumentDestroyNodeList(AjPDomDocument doc, AjPDomNodeList *Plist,
874                                   AjBool donodes)
875 {
876     AjPDomNodeList list;
877     AjPDomNodeEntry entry = NULL;
878     AjPDomNodeEntry tmp   = NULL;
879 
880     if(!Plist)
881         return;
882 
883     if(!*Plist)
884         return;
885 
886     list = *Plist;
887 
888     if(list)
889     {
890         if(list->filter)
891         {
892             entry = list->first;
893 
894             while(entry)
895             {
896                 if(donodes)
897                     ajDomDocumentDestroyNode(doc, &entry->node);
898 
899                 tmp = entry;
900                 entry = entry->next;
901                 AJFREE(tmp);
902             }
903         }
904 
905         if(list->table)
906         {
907             if(donodes)
908                 ajTableMapDel(list->table, &domClearMapAll, (void *) doc);
909             else
910                 ajTableMapDel(list->table, &domClearMapValues, NULL);
911 
912             ajTableFree(&list->table);
913         }
914 
915         AJFREE(*Plist);
916     }
917 
918     return;
919 }
920 
921 
922 
923 
924 /* @funcstatic domClearMapValues **********************************************
925 **
926 ** Clear only the values from the map table
927 **
928 ** @param [r] key [void**] Standard argument, table key.
929 ** @param [r] value [void**] Standard argument, table data item.
930 ** @param [r] cl [void*] Standard argument, usually NULL
931 **
932 ** @return [void]
933 **
934 ** @release 6.3.0
935 ** @@
936 ******************************************************************************/
937 
domClearMapValues(void ** key,void ** value,void * cl)938 static void domClearMapValues(void **key, void **value, void *cl)
939 {
940     AjPDomNodeEntry entry = NULL;
941 
942     entry = (AjPDomNodeEntry) *value;
943     AJFREE(entry);
944 
945     (void) cl;                          /* make it used */
946     (void) key;
947 
948     *value = NULL;
949 
950     return;
951 }
952 
953 
954 
955 
956 /* @funcstatic domClearMapAll *************************************************
957 **
958 ** Clear keys and values from the map table
959 **
960 ** @param [r] key [void**] Standard argument, table key.
961 ** @param [r] value [void**] Standard argument, table data item.
962 ** @param [r] cl [void*] Standard argument, usually NULL
963 **
964 ** @return [void]
965 **
966 ** @release 6.4.0
967 ** @@
968 ******************************************************************************/
969 
domClearMapAll(void ** key,void ** value,void * cl)970 static void domClearMapAll(void **key, void **value, void *cl)
971 {
972     AjPDomNodeEntry entry = NULL;
973     AjPDomNode node = NULL;
974     AjPDomDocument doc = NULL;
975 
976     entry = (AjPDomNodeEntry) *value;
977     AJFREE(entry);
978 
979     node = (AjPDomNode) *key;
980 
981     doc = (AjPDomDocument) cl;
982 
983     ajDomDocumentDestroyNode(doc, &node);
984 
985     *value = NULL;
986 
987     return;
988 }
989 
990 
991 
992 
993 /* @func ajDomCreateNodeList **************************************************
994 **
995 ** Create a node list
996 **
997 ** @param [u] doc [AjPDomDocument] document
998 ** @return [AjPDomNodeList] new list
999 **
1000 ** @release 4.0.0
1001 ** @@
1002 ******************************************************************************/
1003 
ajDomCreateNodeList(AjPDomDocument doc)1004 AjPDomNodeList ajDomCreateNodeList(AjPDomDocument doc)
1005 {
1006     AjPDomNodeList list = NULL;
1007 
1008     AJNEW0(list);
1009     list->ownerdocument = doc;
1010 
1011     return list;
1012 }
1013 
1014 
1015 
1016 
1017 /* @func ajDomDocumentCreateNode **********************************************
1018 **
1019 ** General document node creation
1020 **
1021 ** @param [u] doc [AjPDomDocument] document
1022 ** @param [u] nodetype [AjEDomNodeType] AJAX DOM Node Type enumeration
1023 ** @return [AjPDomNode] new node
1024 **
1025 ** @release 4.0.0
1026 ** @@
1027 ******************************************************************************/
1028 
ajDomDocumentCreateNode(AjPDomDocument doc,AjEDomNodeType nodetype)1029 AjPDomNode ajDomDocumentCreateNode(AjPDomDocument doc,
1030                                    AjEDomNodeType nodetype)
1031 {
1032     AjPDomNode node;
1033 
1034     if (!doc && nodetype != ajEDomNodeTypeDocumentNode &&
1035         nodetype != ajEDomNodeTypeDocumentType)
1036     {
1037         ajDebug("ajDocumentCreateNode: allocation failure\n");
1038         return NULL;
1039     }
1040 
1041     AJNEW0(node);
1042 
1043     node->ownerdocument = doc;
1044     node->type          = nodetype;
1045 
1046     switch (nodetype)
1047     {
1048         case ajEDomNodeTypeDocumentNode:
1049         case ajEDomNodeTypeDocumentType:
1050         case ajEDomNodeTypeElement:
1051         case ajEDomNodeTypeAttribute:
1052         case ajEDomNodeTypeEntityReference:
1053         case ajEDomNodeTypeEntityNode:
1054         case ajEDomNodeTypeDocumentFragment:
1055             node->childnodes = ajDomCreateNodeList(doc);
1056 
1057             if(node->childnodes == NULL)
1058             {
1059                 ajDebug("ajDocumentCreateNode: ajDocumentCreateNodeList"
1060                         " failed\n");
1061                 ajDomDocumentDestroyNode(doc, &node);
1062                 return NULL;
1063             }
1064         default:
1065             ajDebug("ajDomDocumentCreateNode got unexpected "
1066                     "AJAX DOM Node type %d.\n", nodetype);
1067     }
1068 
1069     return node;
1070 }
1071 
1072 
1073 
1074 
1075 /* @func ajDomImplementationCreateDocumentType ********************************
1076 **
1077 ** Creates an empty DocumentType node into which entities/notations (etc)
1078 ** can be placed
1079 **
1080 ** @param [r] qualname [const AjPStr] qualified name
1081 ** @param [r] publicid [const AjPStr] public id
1082 ** @param [r] systemid [const AjPStr] systemid
1083 ** @return [AjPDomDocumentType] new DocumentType node
1084 **
1085 ** @release 4.0.0
1086 ** @@
1087 ******************************************************************************/
1088 
ajDomImplementationCreateDocumentType(const AjPStr qualname,const AjPStr publicid,const AjPStr systemid)1089 AjPDomDocumentType ajDomImplementationCreateDocumentType(const AjPStr qualname,
1090                                                          const AjPStr publicid,
1091                                                          const AjPStr systemid)
1092 {
1093     char *p = NULL;
1094     char *s = NULL;
1095     char *r = NULL;
1096 
1097     if(qualname)
1098         p = MAJSTRGETPTR(qualname);
1099 
1100     if(publicid)
1101         r = MAJSTRGETPTR(publicid);
1102 
1103     if(systemid)
1104         s = MAJSTRGETPTR(systemid);
1105 
1106     return ajDomImplementationCreateDocumentTypeC(p, r, s);
1107 }
1108 
1109 
1110 
1111 
1112 /* @func ajDomImplementationCreateDocumentTypeC *******************************
1113 **
1114 ** Creates an empty DocumentType node into which entities/notations (etc)
1115 ** can be placed
1116 **
1117 ** @param [r] qualname [const char *] qualified name
1118 ** @param [r] publicid [const char *] public id
1119 ** @param [r] systemid [const char *] systemid
1120 ** @return [AjPDomDocumentType] new DocumentType node
1121 **
1122 ** @release 4.0.0
1123 ** @@
1124 ******************************************************************************/
1125 
ajDomImplementationCreateDocumentTypeC(const char * qualname,const char * publicid,const char * systemid)1126 AjPDomDocumentType ajDomImplementationCreateDocumentTypeC(const char *qualname,
1127                                                           const char *publicid,
1128                                                           const char *systemid)
1129 {
1130     AjPDomDocumentType doctype;
1131     AjPDomNodeMap entities;
1132     AjPDomNodeMap notations;
1133 
1134 
1135     if(!(doctype = ajDomDocumentCreateNode(NULL, ajEDomNodeTypeDocumentType)))
1136     {
1137         ajDebug("ajDomImplementationCreateDocumentType: Cannot create node");
1138         return NULL;
1139     }
1140 
1141     ajStrAssignC(&doctype->sub.DocumentType.name, qualname);
1142     doctype->name = doctype->sub.DocumentType.name;
1143 
1144     if(publicid)
1145         ajStrAssignC(&doctype->sub.DocumentType.publicid, publicid);
1146 
1147     if(systemid)
1148         ajStrAssignC(&doctype->sub.DocumentType.systemid, systemid);
1149 
1150 
1151     entities  = ajDomCreateNodeList(NULL);
1152     notations = ajDomCreateNodeList(NULL);
1153 
1154     entities->filter = ajEDomNodeTypeEntityNode;
1155     entities->list   = doctype->childnodes;
1156 
1157     notations->filter = ajEDomNodeTypeNotation;
1158     notations->list   = doctype->childnodes;
1159 
1160     doctype->sub.DocumentType.entities = entities;
1161     doctype->sub.DocumentType.notations = notations;
1162 
1163     return doctype;
1164 }
1165 
1166 
1167 
1168 
1169 /* @func ajDomImplementationCreateDocument ************************************
1170 **
1171 ** Creates an XML Document object of the specified type with its document
1172 ** element.
1173 **
1174 ** @param [r] uri [const AjPStr] uri (not implemented. Pass NULL)
1175 ** @param [r] qualname [const AjPStr] qualified name
1176 ** @param [u] doctype [AjPDomDocumentType] doctype
1177 ** @return [AjPDomDocument] new document
1178 **
1179 ** @release 4.0.0
1180 ** @@
1181 ******************************************************************************/
1182 
ajDomImplementationCreateDocument(const AjPStr uri,const AjPStr qualname,AjPDomDocumentType doctype)1183 AjPDomDocument ajDomImplementationCreateDocument(const AjPStr uri,
1184                                                  const AjPStr qualname,
1185                                                  AjPDomDocumentType doctype)
1186 {
1187     const char *p = NULL;
1188 
1189     (void) uri;                         /* temporary use */
1190 
1191     if(qualname)
1192         p = MAJSTRGETPTR(qualname);
1193 
1194     return ajDomImplementationCreateDocumentC(NULL, p, doctype);
1195 }
1196 
1197 
1198 
1199 
1200 /* @func ajDomImplementationCreateDocumentC ***********************************
1201 **
1202 ** Creates an XML Document object of the specified type with its document
1203 ** element.
1204 **
1205 ** @param [r] uri [const char *] uri (not implemented. Pass NULL)
1206 ** @param [r] qualname [const char *] qualified name
1207 ** @param [u] doctype [AjPDomDocumentType] doctype
1208 ** @return [AjPDomDocument] new document
1209 **
1210 ** @release 4.0.0
1211 ** @@
1212 ******************************************************************************/
1213 
ajDomImplementationCreateDocumentC(const char * uri,const char * qualname,AjPDomDocumentType doctype)1214 AjPDomDocument ajDomImplementationCreateDocumentC(const char *uri,
1215                                                   const char *qualname,
1216                                                   AjPDomDocumentType doctype)
1217 {
1218     AjPDomDocument doc     = NULL;
1219     AjPDomElement  element = NULL;
1220 
1221     (void) uri;
1222 
1223     if(!(doc = ajDomDocumentCreateNode(NULL, ajEDomNodeTypeDocumentNode)))
1224     {
1225         ajDebug("ajDomImplementationCreateDocumentC: document memory\n");
1226         return NULL;
1227     }
1228 
1229     doc->name = ajStrNew();
1230     ajStrAssignC(&doc->name, "#document");
1231 
1232     if(doctype)
1233         ajDomNodeAppendChild(doc, doctype);
1234 
1235     if(qualname)
1236         if(strlen(qualname))
1237         {
1238             element = ajDomDocumentCreateElementC(doc, qualname);
1239             if(!element)
1240             {
1241                 ajDebug("ajDomImplementationCreateDocumentC: element memory\n");
1242                 ajDomDocumentDestroyNode(doc, &doc);
1243 
1244                 return NULL;
1245             }
1246 
1247             ajDomNodeAppendChild(doc, element);
1248         }
1249 
1250     return doc;
1251 }
1252 
1253 
1254 
1255 
1256 /* @func ajDomNodeMapGetItem **************************************************
1257 **
1258 ** Returns the named node from a node map
1259 **
1260 ** @param [r] map [const AjPDomNodeMap] map
1261 ** @param [r] name [const AjPStr] name
1262 ** @return [AjPDomNode] node
1263 **
1264 ** @release 4.0.0
1265 ** @@
1266 ******************************************************************************/
1267 
ajDomNodeMapGetItem(const AjPDomNodeMap map,const AjPStr name)1268 AjPDomNode ajDomNodeMapGetItem(const AjPDomNodeMap map, const AjPStr name)
1269 {
1270     char *p = NULL;
1271 
1272     if(name)
1273         p = MAJSTRGETPTR(name);
1274 
1275     return ajDomNodeMapGetItemC(map, p);
1276 }
1277 
1278 
1279 
1280 
1281 /* @func ajDomNodeMapGetItemC *************************************************
1282 **
1283 ** Returns the named node from a node map
1284 **
1285 ** @param [r] map [const AjPDomNodeMap] map
1286 ** @param [r] name [const char *] name
1287 ** @return [AjPDomNode] node
1288 **
1289 ** @release 4.0.0
1290 ** @@
1291 ******************************************************************************/
1292 
ajDomNodeMapGetItemC(const AjPDomNodeMap map,const char * name)1293 AjPDomNode ajDomNodeMapGetItemC(const AjPDomNodeMap map, const char *name)
1294 {
1295     AjPDomNodeEntry e = NULL;
1296     ajuint nodetype;
1297 
1298     if(map && name)
1299     {
1300         if(map->filter)
1301         {
1302             nodetype = map->filter;
1303             map = map->list;
1304             for(e = map->first; e; e = e->next)
1305                 if(e->node->type == nodetype &&
1306                    !strcmp(name, MAJSTRGETPTR(e->node->name)))
1307                     return e->node;
1308         }
1309         else
1310             for(e = map->first; e; e = e->next)
1311                 if(!strcmp(name, MAJSTRGETPTR(e->node->name)))
1312                     return e->node;
1313     }
1314 
1315     return NULL;
1316 }
1317 
1318 
1319 
1320 
1321 /* @func ajDomElementGetAttribute *********************************************
1322 **
1323 ** Returns the value of a named attribute
1324 **
1325 ** @param [r] element [const AjPDomElement] element
1326 ** @param [r] name [const AjPStr] name
1327 ** @return [AjPStr] value or empty string
1328 **
1329 ** @release 4.0.0
1330 ** @@
1331 ******************************************************************************/
1332 
ajDomElementGetAttribute(const AjPDomElement element,const AjPStr name)1333 AjPStr ajDomElementGetAttribute(const AjPDomElement element, const AjPStr name)
1334 {
1335     char *p = NULL;
1336 
1337     if(name)
1338         p = MAJSTRGETPTR(name);
1339 
1340     return ajDomElementGetAttributeC(element, p);
1341 }
1342 
1343 
1344 
1345 
1346 /* @func ajDomElementGetAttributeC ********************************************
1347 **
1348 ** Returns the value of a named attribute
1349 **
1350 ** @param [r] element [const AjPDomElement] element
1351 ** @param [r] name [const char *] name
1352 ** @return [AjPStr] value or empty string
1353 **
1354 ** @release 4.0.0
1355 ** @@
1356 ******************************************************************************/
1357 
ajDomElementGetAttributeC(const AjPDomElement element,const char * name)1358 AjPStr ajDomElementGetAttributeC(const AjPDomElement element,
1359                                  const char *name)
1360 {
1361     AjPDomNode node = NULL;
1362     AjPStr ret = NULL;
1363 
1364     ret = ajStrNewC("");
1365 
1366     if(element && name && element->attributes)
1367     {
1368         if((node = ajDomNodeMapGetItemC(element->attributes, name)))
1369             ajStrAssignS(&ret, node->value);
1370     }
1371 
1372     return ret;
1373 }
1374 
1375 
1376 
1377 
1378 /* @func ajDomNodeMapSetItem **************************************************
1379 **
1380 ** Enter arg into the node map using name as the key.
1381 ** If a node with the same name already exists in the map it will be replaced
1382 ** with the new node and returned.
1383 ** The replaced node should usually be freed with DestroyNode.
1384 **
1385 ** @param [u] map [AjPDomNodeMap] map
1386 ** @param [u] arg [AjPDomNode] arg
1387 ** @return [AjPDomNode] replaced node or NULL
1388 **
1389 ** @release 4.0.0
1390 ** @@
1391 ******************************************************************************/
1392 
ajDomNodeMapSetItem(AjPDomNodeMap map,AjPDomNode arg)1393 AjPDomNode ajDomNodeMapSetItem(AjPDomNodeMap map, AjPDomNode arg)
1394 {
1395     AjPDomNodeEntry e = NULL;
1396     AjPDomNode tmp = NULL;
1397 
1398     if(map && arg)
1399     {
1400         if(map->filter)
1401         {
1402             ajDebug("ajDomNodeMapSetItem: No mod allowed\n");
1403 
1404             return NULL;
1405         }
1406 
1407         if(map->ownerdocument != arg->ownerdocument)
1408         {
1409             ajDebug("ajDomNodeMapSetItem: Wrong document\n");
1410 
1411             return NULL;
1412         }
1413 
1414         if(arg->type == ajEDomNodeTypeAttribute &&
1415            arg->sub.Attr.ownerelement &&
1416            arg->sub.Attr.ownerelement != map->ownerelement)
1417         {
1418             ajDebug("ajDomNodeMapSetItem: In use attribute error\n");
1419 
1420             return NULL;
1421         }
1422 
1423         for(e = map->first; e && !ajStrMatchS(arg->name, e->node->name);
1424             e = e->next)
1425         {
1426             ;
1427         }
1428 
1429         if(e)
1430         {
1431             tmp = e->node;
1432             e->node = arg;
1433             if(arg->type == ajEDomNodeTypeAttribute)
1434             {
1435                 arg->sub.Attr.ownerelement = map->ownerelement;
1436                 tmp->sub.Attr.ownerelement = NULL;
1437             }
1438 
1439             return tmp;
1440         }
1441 
1442         ajDomNodeListAppend(map, arg);
1443     }
1444 
1445     return NULL;
1446 }
1447 
1448 
1449 
1450 
1451 /* @func ajDomNodeMapRemoveItem ***********************************************
1452 **
1453 ** Removes and returns item from a  map
1454 **
1455 ** @param [u] map [AjPDomNodeMap] map
1456 ** @param [r] name [const AjPStr] name
1457 ** @return [AjPDomNode] removed node or NULL
1458 **
1459 ** @release 4.0.0
1460 ** @@
1461 ******************************************************************************/
1462 
ajDomNodeMapRemoveItem(AjPDomNodeMap map,const AjPStr name)1463 AjPDomNode ajDomNodeMapRemoveItem(AjPDomNodeMap map, const AjPStr name)
1464 {
1465     char *p = NULL;
1466 
1467     if(name)
1468         p = MAJSTRGETPTR(name);
1469 
1470     return ajDomNodeMapRemoveItemC(map, p);
1471 }
1472 
1473 
1474 
1475 
1476 /* @func ajDomNodeMapRemoveItemC **********************************************
1477 **
1478 ** Removes and returns item from a  map
1479 **
1480 ** @param [w] map [AjPDomNodeMap] map
1481 ** @param [r] name [const char *] name
1482 ** @return [AjPDomNode] removed node or NULL
1483 **
1484 ** @release 4.0.0
1485 ** @@
1486 ******************************************************************************/
1487 
ajDomNodeMapRemoveItemC(AjPDomNodeMap map,const char * name)1488 AjPDomNode ajDomNodeMapRemoveItemC(AjPDomNodeMap map, const char *name)
1489 {
1490     AjPDomNodeEntry e = NULL;
1491     AjPDomNode r = NULL;
1492 
1493     if(map && strlen(name))
1494     {
1495         if(map->filter)
1496         {
1497             ajDebug("ajDomNodeMapRemoveItemC: No mod allowed\n");
1498 
1499             return NULL;
1500         }
1501 
1502         for(e = map->first; e; e = e->next)
1503         {
1504             if(!strcmp(name, MAJSTRGETPTR(e->node->name)) &&
1505                ajDomNodeListRemove(map, e->node))
1506             {
1507                 r = e->node;
1508                 free(e);
1509 
1510                 if(r->type == ajEDomNodeTypeAttribute)
1511                     r->sub.Attr.ownerelement = NULL;
1512 
1513                 return r;
1514             }
1515         }
1516     }
1517 
1518     ajDebug("ajDomNodeMapRemoveItemC: Not found error\n");
1519 
1520     return NULL;
1521 }
1522 
1523 
1524 
1525 
1526 /* @func ajDomNodeMapItem *****************************************************
1527 **
1528 ** Return the node in the map at a given index
1529 **
1530 ** @param [r] map [const AjPDomNodeMap] map
1531 ** @param [r] indexnum [ajint] index
1532 ** @return [AjPDomNode] node or NULL
1533 **
1534 ** @release 4.0.0
1535 ** @@
1536 ******************************************************************************/
1537 
ajDomNodeMapItem(const AjPDomNodeMap map,ajint indexnum)1538 AjPDomNode ajDomNodeMapItem(const AjPDomNodeMap map, ajint indexnum)
1539 {
1540     return ajDomNodeListItem(map, indexnum);
1541 }
1542 
1543 
1544 
1545 
1546 /* @funcstatic domNodeListItemFiltered ****************************************
1547 **
1548 ** Filter node list item
1549 **
1550 ** @param [r] list [const AjPDomNodeList] list
1551 ** @param [r] indexnum [ajint] index
1552 ** @param [r] nodetype [ajuint] nodetype
1553 ** @return [AjPDomNode] node or NULL
1554 **
1555 ** @release 4.0.0
1556 ** @@
1557 ******************************************************************************/
1558 
domNodeListItemFiltered(const AjPDomNodeList list,ajint indexnum,ajuint nodetype)1559 static AjPDomNode domNodeListItemFiltered(const AjPDomNodeList list,
1560                                           ajint indexnum, ajuint nodetype)
1561 {
1562     AjPDomNodeEntry e = NULL;
1563 
1564     if(list && indexnum >= 0 && indexnum < list->length)
1565         for(e = list->first; e; e = e->next)
1566             if(e->node->type == nodetype)
1567             {
1568                 if(!indexnum)
1569                     return e->node;
1570 
1571                 --indexnum;
1572             }
1573 
1574     return NULL;
1575 }
1576 
1577 
1578 
1579 
1580 /* @func ajDomNodeListItem ****************************************************
1581 **
1582 ** Return the node in the list at a given index
1583 **
1584 ** @param [r] list [const AjPDomNodeList] list
1585 ** @param [r] indexnum [ajint] index
1586 ** @return [AjPDomNode] node or NULL
1587 **
1588 ** @release 4.0.0
1589 ** @@
1590 ******************************************************************************/
1591 
ajDomNodeListItem(const AjPDomNodeList list,ajint indexnum)1592 AjPDomNode ajDomNodeListItem(const AjPDomNodeList list, ajint indexnum)
1593 {
1594     AjPDomNodeEntry e = NULL;
1595 
1596     if(list)
1597     {
1598         if(list->filter)
1599             return domNodeListItemFiltered(list->list, indexnum, list->filter);
1600 
1601         if(indexnum >= 0 && indexnum < list->length)
1602             for(e = list->first; e; e = e->next, --indexnum)
1603                 if(!indexnum)
1604                     return e->node;
1605     }
1606 
1607     return NULL;
1608 }
1609 
1610 
1611 
1612 
1613 /* @func ajDomElementSetAttribute *********************************************
1614 **
1615 ** Adds a new attribute or sets the value of an existing attribute.
1616 **
1617 ** @param [r] element [const AjPDomElement] element
1618 ** @param [r] name [const AjPStr] name
1619 ** @param [r] value [const AjPStr] value
1620 ** @return [void]
1621 **
1622 ** @release 4.0.0
1623 ** @@
1624 ******************************************************************************/
1625 
ajDomElementSetAttribute(const AjPDomElement element,const AjPStr name,const AjPStr value)1626 void ajDomElementSetAttribute(const AjPDomElement element, const AjPStr name,
1627                               const AjPStr value)
1628 {
1629     if(!element || !name || !value || !element->attributes)
1630         return;
1631 
1632     if(!ajStrGetLen(name) || !ajStrGetLen(value))
1633         return;
1634 
1635     ajDomElementSetAttributeC(element, MAJSTRGETPTR(name), MAJSTRGETPTR(value));
1636 
1637     return;
1638 }
1639 
1640 
1641 
1642 
1643 /* @func ajDomElementSetAttributeC ********************************************
1644 **
1645 ** Adds a new attribute or sets the value of an existing attribute.
1646 **
1647 ** @param [r] element [const AjPDomElement] element
1648 ** @param [r] name [const char *] name
1649 ** @param [r] value [const char *] value
1650 ** @return [void]
1651 **
1652 ** @release 4.0.0
1653 ** @@
1654 ******************************************************************************/
1655 
ajDomElementSetAttributeC(const AjPDomElement element,const char * name,const char * value)1656 void ajDomElementSetAttributeC(const AjPDomElement element, const char *name,
1657                                const char *value)
1658 {
1659     AjPDomNode attr = NULL;
1660     AjPStr prevvalue = NULL;
1661 
1662 
1663     if(!element || !name || !value || !element->attributes)
1664         return;
1665 
1666     attr = ajDomNodeMapGetItemC(element->attributes, name);
1667 
1668     if(attr)
1669     {
1670         prevvalue = attr->value;
1671 
1672         attr->sub.Attr.value = ajStrNew();
1673         ajStrAssignC(&attr->sub.Attr.value, value);
1674         /* AJB: dirty */
1675         attr->value = attr->sub.Attr.value;
1676     }
1677     else
1678     {
1679         prevvalue = NULL;
1680         if(!(attr = ajDomDocumentCreateAttributeC(element->ownerdocument,
1681                                                   name)))
1682         {
1683             ajDebug("ajDomElementSetAttribute: DOM create failed\n");
1684             return;
1685         }
1686 
1687         ajStrDel(&attr->value);
1688 
1689         attr->sub.Attr.value = ajStrNew();
1690         ajStrAssignC(&attr->sub.Attr.value, value);
1691         /* AJB: dirty */
1692         attr->value = attr->sub.Attr.value;
1693 
1694         ajDomNodeMapSetItem(element->attributes, attr);
1695     }
1696 
1697     domUpdateNode(element->parentnode);
1698 
1699     if(prevvalue)
1700         ajStrDel(&prevvalue);
1701 
1702     return;
1703 }
1704 
1705 
1706 
1707 
1708 /* @func ajDomElementRemoveAttribute ******************************************
1709 **
1710 ** Remove and free a named attribute
1711 **
1712 ** @param [u] element [AjPDomElement] element
1713 ** @param [r] name [const AjPStr] name
1714 ** @return [void]
1715 **
1716 ** @release 4.0.0
1717 ** @@
1718 ******************************************************************************/
1719 
ajDomElementRemoveAttribute(AjPDomElement element,const AjPStr name)1720 void ajDomElementRemoveAttribute(AjPDomElement element, const AjPStr name)
1721 {
1722 
1723     if(!name)
1724         return;
1725 
1726     ajDomElementRemoveAttributeC(element, MAJSTRGETPTR(name));
1727 
1728     return;
1729 }
1730 
1731 
1732 
1733 
1734 /* @func ajDomElementRemoveAttributeC *****************************************
1735 **
1736 ** Remove and free a named attribute
1737 **
1738 ** @param [u] element [AjPDomElement] element
1739 ** @param [r] name [const char *] name
1740 ** @return [void]
1741 **
1742 ** @release 4.0.0
1743 ** @@
1744 ******************************************************************************/
1745 
ajDomElementRemoveAttributeC(AjPDomElement element,const char * name)1746 void ajDomElementRemoveAttributeC(AjPDomElement element, const char *name)
1747 {
1748     AjPDomNode attr = NULL;
1749 
1750     if(!element || !name)
1751         return;
1752 
1753     attr = ajDomNodeMapRemoveItemC(element->attributes, name);
1754 
1755     if(attr)
1756     {
1757         domUpdateNode(element->parentnode);
1758         ajDomDocumentDestroyNode(attr->ownerdocument, &attr);
1759     }
1760 
1761     return;
1762 }
1763 
1764 
1765 
1766 
1767 /* @func ajDomElementGetAttributeNode *****************************************
1768 **
1769 ** Returns the named attribute node.
1770 **
1771 ** @param [r] element [const AjPDomElement] element
1772 ** @param [r] name [const AjPStr] name
1773 ** @return [AjPDomNode] node or NULL
1774 **
1775 ** @release 4.0.0
1776 ** @@
1777 ******************************************************************************/
1778 
ajDomElementGetAttributeNode(const AjPDomElement element,const AjPStr name)1779 AjPDomNode ajDomElementGetAttributeNode(const AjPDomElement element,
1780                                         const AjPStr name)
1781 {
1782     if(element && name)
1783         return ajDomNodeMapGetItem(element->attributes, name);
1784 
1785     return NULL;
1786 }
1787 
1788 
1789 
1790 
1791 /* @func ajDomElementGetAttributeNodeC ****************************************
1792 **
1793 ** Returns the named attribute node.
1794 **
1795 ** @param [r] element [const AjPDomElement] element
1796 ** @param [r] name [const char *] name
1797 ** @return [AjPDomNode] node or NULL
1798 **
1799 ** @release 4.0.0
1800 ** @@
1801 ******************************************************************************/
1802 
ajDomElementGetAttributeNodeC(const AjPDomElement element,const char * name)1803 AjPDomNode ajDomElementGetAttributeNodeC(const AjPDomElement element,
1804                                          const char *name)
1805 {
1806     if(element && name)
1807         return ajDomNodeMapGetItemC(element->attributes, name);
1808 
1809     return NULL;
1810 }
1811 
1812 
1813 
1814 
1815 /* @func ajDomElementSetAttributeNode *****************************************
1816 **
1817 ** Add an attribute to the attributes of an element.
1818 ** If this element already has an attribute with the same name it will be
1819 ** replaced with the new attribute and returned.
1820 **
1821 ** @param [u] element [AjPDomElement] element
1822 ** @param [u] newattr [AjPDomNode] name
1823 ** @return [AjPDomNode] new or replaced attribute
1824 **
1825 ** @release 4.0.0
1826 ** @@
1827 ******************************************************************************/
1828 
ajDomElementSetAttributeNode(AjPDomElement element,AjPDomNode newattr)1829 AjPDomNode ajDomElementSetAttributeNode(AjPDomElement element,
1830                                         AjPDomNode newattr)
1831 {
1832     AjPDomNode attr = NULL;
1833 
1834     if(!element || !newattr)
1835         return NULL;
1836 
1837     if(element->ownerdocument != newattr->ownerdocument)
1838     {
1839         ajDebug("ajDomElementSetAttributeNode: Wrong document\n");
1840         return NULL;
1841     }
1842 
1843     attr = ajDomNodeMapSetItem(element->attributes, newattr);
1844 
1845     domUpdateNode(element->parentnode);
1846 
1847     return attr;
1848 }
1849 
1850 
1851 
1852 
1853 /* @func ajDomElementRemoveAttributeNode **************************************
1854 **
1855 ** Removes and returns a pointer to an attribute
1856 **
1857 ** @param [w] element [AjPDomElement] element
1858 ** @param [u] oldattr [AjPDomNode] attribute
1859 ** @return [AjPDomNode] removed attribute or NULL
1860 **
1861 ** @release 4.0.0
1862 ** @@
1863 ******************************************************************************/
1864 
ajDomElementRemoveAttributeNode(AjPDomElement element,AjPDomNode oldattr)1865 AjPDomNode ajDomElementRemoveAttributeNode(AjPDomElement element,
1866                                            AjPDomNode oldattr)
1867 {
1868     if(!element || !oldattr || !ajDomNodeListRemove(element->attributes,
1869                                                     oldattr))
1870     {
1871         ajDebug("ajDomElementRemoveAttributeNode: DOM not found error\n");
1872         return NULL;
1873     }
1874 
1875     domUpdateNode(element->parentnode);
1876 
1877     return oldattr;
1878 }
1879 
1880 
1881 
1882 
1883 /* @funcstatic domTraverse ****************************************************
1884 **
1885 ** Pre-order elements
1886 **
1887 ** @param [w] list [AjPDomNodeList] list
1888 ** @param [u] node [AjPDomNode] node
1889 ** @param [r] tagname [const AjPStr] tagname
1890 ** @return [void]
1891 **
1892 ** @release 4.0.0
1893 ** @@
1894 ******************************************************************************/
1895 
domTraverse(AjPDomNodeList list,AjPDomNode node,const AjPStr tagname)1896 static void domTraverse(AjPDomNodeList list, AjPDomNode node,
1897                         const AjPStr tagname)
1898 {
1899     AjPDomNode n = NULL;
1900     AjBool doall;
1901 
1902     doall = ajStrMatchC(tagname, "*");
1903 
1904     if(list && node && node->type == ajEDomNodeTypeElement && tagname)
1905     {
1906         if(doall || ajStrMatchS(tagname, node->name))
1907             ajDomNodeListAppend(list, node);
1908 
1909         for(n = node->firstchild; n; n = n->nextsibling)
1910             domTraverse(list, n, tagname);
1911     }
1912 
1913     return;
1914 }
1915 
1916 
1917 
1918 
1919 /* @funcstatic domTraverseC ***************************************************
1920 **
1921 ** Pre-order elements
1922 **
1923 ** @param [w] list [AjPDomNodeList] list
1924 ** @param [u] node [AjPDomNode] node
1925 ** @param [r] tagname [const char *] tagname
1926 ** @return [void]
1927 **
1928 ** @release 4.0.0
1929 ** @@
1930 ******************************************************************************/
1931 
domTraverseC(AjPDomNodeList list,AjPDomNode node,const char * tagname)1932 static void domTraverseC(AjPDomNodeList list, AjPDomNode node,
1933                          const char *tagname)
1934 {
1935     AjPDomNode n = NULL;
1936     AjBool doall = ajFalse;
1937 
1938     doall = ajCharMatchC(tagname, "*");
1939 
1940     if(list && node && node->type == ajEDomNodeTypeElement && tagname)
1941     {
1942         if(doall || !strcmp(tagname, MAJSTRGETPTR(node->name)))
1943             ajDomNodeListAppend(list, node);
1944 
1945         for(n = node->firstchild; n; n = n->nextsibling)
1946             domTraverseC(list, n, tagname);
1947     }
1948 
1949     return;
1950 }
1951 
1952 
1953 
1954 
1955 /* @func ajDomElementGetElementsByTagName *************************************
1956 **
1957 ** Perform a pre-order traversal of the entire document.
1958 ** Return a node list of the elements with the name tagname in the order
1959 ** in which they are found.
1960 **
1961 ** @param [u] element [AjPDomElement] element
1962 ** @param [r] name [const AjPStr] name
1963 ** @return [AjPDomNodeList] node list
1964 **
1965 ** @release 4.0.0
1966 ** @@
1967 ******************************************************************************/
1968 
ajDomElementGetElementsByTagName(AjPDomElement element,const AjPStr name)1969 AjPDomNodeList ajDomElementGetElementsByTagName(AjPDomElement element,
1970                                                 const AjPStr name)
1971 {
1972 
1973     if(!element || !name)
1974         return NULL;
1975 
1976     return ajDomElementGetElementsByTagNameC(element, MAJSTRGETPTR(name));
1977 }
1978 
1979 
1980 
1981 
1982 /* @func ajDomElementGetElementsByTagNameC ************************************
1983 **
1984 ** Perform a pre-order traversal of the entire document.
1985 ** Return a node list of the elements with the name tagname in the order
1986 ** in which they are found.
1987 **
1988 ** @param [u] element [AjPDomElement] element
1989 ** @param [r] name [const char *] name
1990 ** @return [AjPDomNodeList] node list
1991 **
1992 ** @release 4.0.0
1993 ** @@
1994 ******************************************************************************/
1995 
ajDomElementGetElementsByTagNameC(AjPDomElement element,const char * name)1996 AjPDomNodeList ajDomElementGetElementsByTagNameC(AjPDomElement element,
1997                                                  const char *name)
1998 {
1999     AjPDomNodeList list = NULL;
2000     AjPDomNode n = NULL;
2001 
2002     if(element && element->type == ajEDomNodeTypeElement && name &&
2003        (list=ajDomCreateNodeList(element->ownerdocument)))
2004     {
2005         for(n = element->firstchild; n; n = n->nextsibling)
2006             domTraverseC(list, n, name);
2007 
2008         return list;
2009     }
2010 
2011     return NULL;
2012 }
2013 
2014 
2015 
2016 
2017 /* @func ajDomElementNormalise ************************************************
2018 **
2019 ** Merge adjacent text node content into "normal" form in the subtree of
2020 ** a node. Remove empty text node.
2021 **
2022 ** @param [w] element [AjPDomElement] element
2023 ** @return [void]
2024 **
2025 ** @release 4.0.0
2026 ** @@
2027 ******************************************************************************/
2028 
ajDomElementNormalise(AjPDomElement element)2029 void ajDomElementNormalise(AjPDomElement element)
2030 {
2031     AjPDomNode node = NULL;
2032     AjPDomText last = NULL;
2033 
2034 
2035     if(element)
2036     {
2037         for(node = element->firstchild; node; node = node->nextsibling)
2038         {
2039             if(node->type == ajEDomNodeTypeText)
2040             {
2041                 if(last)
2042                 {
2043                     ajDomCharacterDataInsertData(node, 0, last->value);
2044                     ajDomRemoveChild(element, last);
2045                     ajDomDocumentDestroyNode(last->ownerdocument, &last);
2046                 }
2047 
2048                 last = node;
2049             }
2050             else
2051             {
2052                 last = NULL;
2053 
2054                 ajDomElementNormalise(node);
2055             }
2056 
2057             /* AJB: Would do a return here if exception */
2058         }
2059     }
2060 
2061     return;
2062 }
2063 
2064 
2065 
2066 
2067 /* @func ajDomCharacterDataSubstringData **************************************
2068 **
2069 ** Return a string representing the substring beginning at the character at
2070 ** offset with a length of count.
2071 ** If the sum of offset and count exceeds the length of the character data,
2072 ** a string representing the remainder of the string is returned.
2073 **
2074 ** @param [r] data [const AjPDomCharacterData] character data
2075 ** @param [r] offset [ajint] offset
2076 ** @param [r] count [ajint] count
2077 
2078 ** @return [AjPStr] substring
2079 **
2080 ** @release 4.0.0
2081 ** @@
2082 ******************************************************************************/
2083 
ajDomCharacterDataSubstringData(const AjPDomCharacterData data,ajint offset,ajint count)2084 AjPStr ajDomCharacterDataSubstringData(const AjPDomCharacterData data,
2085                                        ajint offset,
2086                                        ajint count)
2087 {
2088     AjPStr sub = NULL;
2089     ajint dlen = 0;
2090 
2091     if(!data)
2092         return NULL;
2093 
2094     if(offset < 0 || offset > (dlen = data->sub.CharacterData.length) ||
2095        count < 0)
2096         return NULL;
2097 
2098     if(count > (dlen - offset))
2099         count = dlen - offset;
2100 
2101     sub = ajStrNew();
2102 
2103     ajStrAssignSubS(&sub, data->value, offset, offset + count - 1);
2104 
2105     return sub;
2106 }
2107 
2108 
2109 
2110 
2111 /* @func ajDomCharacterDataAppendData *****************************************
2112 **
2113 ** Append a string to a character data node
2114 **
2115 ** @param [w] data [AjPDomCharacterData] character data
2116 ** @param [r] arg [const AjPStr] string
2117 ** @return [void]
2118 **
2119 ** @release 4.0.0
2120 ** @@
2121 ******************************************************************************/
2122 
ajDomCharacterDataAppendData(AjPDomCharacterData data,const AjPStr arg)2123 void ajDomCharacterDataAppendData(AjPDomCharacterData data, const AjPStr arg)
2124 {
2125 
2126     if(!data || !arg)
2127         return;
2128 
2129     ajDomCharacterDataAppendDataC(data, MAJSTRGETPTR(arg));
2130 
2131     return;
2132 }
2133 
2134 
2135 
2136 
2137 /* @func ajDomCharacterDataAppendDataC ****************************************
2138 **
2139 ** Append a string to a character data node
2140 **
2141 ** @param [w] data [AjPDomCharacterData] character data
2142 ** @param [r] arg [const char *] string
2143 ** @return [void]
2144 **
2145 ** @release 4.0.0
2146 ** @@
2147 ******************************************************************************/
2148 
ajDomCharacterDataAppendDataC(AjPDomCharacterData data,const char * arg)2149 void ajDomCharacterDataAppendDataC(AjPDomCharacterData data, const char *arg)
2150 {
2151 
2152     if(!data || !arg)
2153         return;
2154 
2155     ajStrAppendC(&data->value, arg);
2156     data->sub.CharacterData.length = ajStrGetLen(data->value);
2157     data->sub.CharacterData.data = data->value;
2158 
2159     domUpdateNode(data->parentnode);
2160 
2161     return;
2162 }
2163 
2164 
2165 
2166 
2167 /* @func ajDomCharacterDataInsertData *****************************************
2168 **
2169 ** Insert a string at position offset
2170 **
2171 ** @param [w] data [AjPDomCharacterData] character data
2172 ** @param [r] offset [ajint] offset
2173 ** @param [r] arg [const AjPStr] string
2174 ** @return [void]
2175 **
2176 ** @release 4.0.0
2177 ** @@
2178 ******************************************************************************/
2179 
ajDomCharacterDataInsertData(AjPDomCharacterData data,ajint offset,const AjPStr arg)2180 void ajDomCharacterDataInsertData(AjPDomCharacterData data, ajint offset,
2181                                   const AjPStr arg)
2182 {
2183 
2184     if(!data || !arg)
2185         return;
2186 
2187     ajDomCharacterDataInsertDataC(data, offset, MAJSTRGETPTR(arg));
2188 
2189     return;
2190 }
2191 
2192 
2193 
2194 
2195 /* @func ajDomCharacterDataInsertDataC ****************************************
2196 **
2197 ** Insert a string at position offset
2198 **
2199 ** @param [w] data [AjPDomCharacterData] character data
2200 ** @param [r] offset [ajint] offset
2201 ** @param [r] arg [const char *] string
2202 ** @return [void]
2203 **
2204 ** @release 4.0.0
2205 ** @@
2206 ******************************************************************************/
2207 
ajDomCharacterDataInsertDataC(AjPDomCharacterData data,ajint offset,const char * arg)2208 void ajDomCharacterDataInsertDataC(AjPDomCharacterData data, ajint offset,
2209                                    const char *arg)
2210 {
2211 
2212     if(!data || !arg)
2213         return;
2214 
2215     if(offset < 0 || offset > data->sub.CharacterData.length)
2216         return;
2217 
2218     ajStrInsertC(&data->value, offset, arg);
2219     data->sub.CharacterData.length = ajStrGetLen(data->value);
2220     data->sub.CharacterData.data = data->value;
2221 
2222     domUpdateNode(data->parentnode);
2223 
2224     return;
2225 }
2226 
2227 
2228 
2229 
2230 /* @func ajDomCharacterDataDeleteData *****************************************
2231 **
2232 ** Remove at most count characters of position offset
2233 **
2234 ** @param [w] data [AjPDomCharacterData] character data
2235 ** @param [r] offset [ajint] offset
2236 ** @param [r] count [ajint] count
2237 ** @return [void]
2238 **
2239 ** @release 4.0.0
2240 ** @@
2241 ******************************************************************************/
2242 
ajDomCharacterDataDeleteData(AjPDomCharacterData data,ajint offset,ajint count)2243 void ajDomCharacterDataDeleteData(AjPDomCharacterData data, ajint offset,
2244                                   ajint count)
2245 {
2246     ajint dlen = 0;
2247 
2248     if(!data)
2249         return;
2250 
2251     dlen = ajStrGetLen(data->value);
2252 
2253     if(offset <0 || offset > dlen)
2254         return;
2255 
2256     if(count < 0 || (offset + count) > dlen)
2257         count = dlen - offset;
2258 
2259     ajStrCutRange(&data->value, offset, offset + count - 1);
2260     data->sub.CharacterData.length = ajStrGetLen(data->value);
2261     data->sub.CharacterData.data = data->value;
2262 
2263     domUpdateNode(data->parentnode);
2264 
2265     return;
2266 }
2267 
2268 
2269 
2270 
2271 /* @func ajDomCharacterDataReplaceData ****************************************
2272 **
2273 ** Replace offset/count characters with a string
2274 **
2275 ** @param [w] data [AjPDomCharacterData] character data
2276 ** @param [r] offset [ajint] offset
2277 ** @param [r] count [ajint] count
2278 ** @param [r] arg [const AjPStr] replacement string
2279 ** @return [void]
2280 **
2281 ** @release 4.0.0
2282 ** @@
2283 ******************************************************************************/
2284 
ajDomCharacterDataReplaceData(AjPDomCharacterData data,ajint offset,ajint count,const AjPStr arg)2285 void ajDomCharacterDataReplaceData(AjPDomCharacterData data, ajint offset,
2286                                    ajint count, const AjPStr arg)
2287 {
2288     ajDomCharacterDataDeleteData(data, offset, count);
2289     ajDomCharacterDataInsertData(data, offset, arg);
2290 
2291     return;
2292 }
2293 
2294 
2295 
2296 
2297 /* @func ajDomCharacterDataReplaceDataC ***************************************
2298 **
2299 ** Replace offset/count characters with a string
2300 **
2301 ** @param [w] data [AjPDomCharacterData] character data
2302 ** @param [r] offset [ajint] offset
2303 ** @param [r] count [ajint] count
2304 ** @param [r] arg [const char *] replacement string
2305 ** @return [void]
2306 **
2307 ** @release 4.0.0
2308 ** @@
2309 ******************************************************************************/
2310 
ajDomCharacterDataReplaceDataC(AjPDomCharacterData data,ajint offset,ajint count,const char * arg)2311 void ajDomCharacterDataReplaceDataC(AjPDomCharacterData data, ajint offset,
2312                                     ajint count, const char *arg)
2313 {
2314     ajDomCharacterDataDeleteData(data, offset, count);
2315     ajDomCharacterDataInsertDataC(data, offset, arg);
2316 
2317     return;
2318 }
2319 
2320 
2321 
2322 
2323 /* @func ajDomCharacterDataGetLength ******************************************
2324 **
2325 ** Return length of character data
2326 **
2327 ** @param [r] data [const AjPDomCharacterData] character data
2328 ** @return [ajint] length
2329 **
2330 ** @release 4.0.0
2331 ** @@
2332 ******************************************************************************/
2333 
ajDomCharacterDataGetLength(const AjPDomCharacterData data)2334 ajint ajDomCharacterDataGetLength(const AjPDomCharacterData data)
2335 {
2336     return data ? data->sub.CharacterData.length : 0;
2337 }
2338 
2339 
2340 
2341 
2342 /* @func ajDomTextSplitText ***************************************************
2343 **
2344 ** Split a text node at the specified offset into two adjacent text nodes.
2345 ** The first is a string offset size in length whereas the second is the
2346 ** remainder. If offset is equal to the length of the string the new sibling
2347 ** has zero length
2348 **
2349 ** @param [w] text [AjPDomText] text
2350 ** @param [r] offset [ajint] offset
2351 ** @return [AjPDomText] remainder node or NULL
2352 **
2353 ** @release 4.0.0
2354 ** @@
2355 ******************************************************************************/
2356 
ajDomTextSplitText(AjPDomText text,ajint offset)2357 AjPDomText ajDomTextSplitText(AjPDomText text, ajint offset)
2358 {
2359     AjPDomText node;
2360     AjPStr sub = NULL;
2361     ajint len = 0;
2362 
2363     if(!text || !text->parentnode)
2364         return NULL;
2365 
2366     if(offset < 0 || offset > text->sub.CharacterData.length)
2367         return NULL;
2368 
2369     sub = ajStrNew();
2370     len = ajStrGetLen(text->value);
2371 
2372     ajStrAssignSubS(&sub, text->value, offset, len - 1);
2373 
2374     node = ajDomDocumentCreateTextNode(text->ownerdocument, sub);
2375 
2376     ajStrDel(&sub);
2377 
2378     if(!node)
2379         return NULL;
2380 
2381     ajDomCharacterDataDeleteData(text, offset, len - 1);
2382     ajDomNodeInsertBefore(text->parentnode, node, text->nextsibling);
2383 
2384     return node;
2385 }
2386 
2387 
2388 
2389 
2390 /* @func ajDomDocumentCreateElement *******************************************
2391 **
2392 ** Create a document element
2393 **
2394 ** @param [w] doc [AjPDomDocument] document
2395 ** @param [r] tagname [const AjPStr] tagname
2396 ** @return [AjPDomElement] element
2397 **
2398 ** @release 4.0.0
2399 ** @@
2400 ******************************************************************************/
2401 
ajDomDocumentCreateElement(AjPDomDocument doc,const AjPStr tagname)2402 AjPDomElement ajDomDocumentCreateElement(AjPDomDocument doc,
2403                                          const AjPStr tagname)
2404 {
2405     if(!tagname)
2406         return NULL;
2407 
2408     return ajDomDocumentCreateElementC(doc, MAJSTRGETPTR(tagname));
2409 }
2410 
2411 
2412 
2413 
2414 /* @func ajDomDocumentCreateElementC ******************************************
2415 **
2416 ** Create a document element
2417 **
2418 ** @param [w] doc [AjPDomDocument] document
2419 ** @param [r] tagname [const char *] tagname
2420 ** @return [AjPDomElement] element
2421 **
2422 ** @release 4.0.0
2423 ** @@
2424 ******************************************************************************/
2425 
ajDomDocumentCreateElementC(AjPDomDocument doc,const char * tagname)2426 AjPDomElement ajDomDocumentCreateElementC(AjPDomDocument doc,
2427                                           const char *tagname)
2428 {
2429     AjPDomElement element = NULL;
2430 
2431     if(!tagname)
2432         return NULL;
2433 
2434     element = ajDomDocumentCreateNode(doc, ajEDomNodeTypeElement);
2435 
2436     if(!element)
2437         return NULL;
2438 
2439     element->sub.Element.tagname = ajStrNewC(tagname);
2440     element->name = element->sub.Element.tagname;
2441 
2442     element->attributes = ajDomCreateNodeList(doc);
2443 
2444     element->attributes->ownerelement = element;
2445 
2446     return element;
2447 }
2448 
2449 
2450 
2451 
2452 /* @func ajDomDocumentCreateDocumentFragment **********************************
2453 **
2454 ** Create an empty document fragment
2455 **
2456 ** @param [w] doc [AjPDomDocument] document
2457 ** @return [AjPDomDocumentFragment] fragment
2458 **
2459 ** @release 4.0.0
2460 ** @@
2461 ******************************************************************************/
2462 
ajDomDocumentCreateDocumentFragment(AjPDomDocument doc)2463 AjPDomDocumentFragment ajDomDocumentCreateDocumentFragment(AjPDomDocument doc)
2464 {
2465     AjPDomDocumentFragment frag = NULL;
2466 
2467     frag = ajDomDocumentCreateNode(doc, ajEDomNodeTypeDocumentFragment);
2468 
2469     if(frag)
2470         frag->name = ajStrNewC("#document-fragment");
2471 
2472     return frag;
2473 }
2474 
2475 
2476 
2477 
2478 /* @func ajDomDocumentCreateTextNode ******************************************
2479 **
2480 ** Create a  text node
2481 **
2482 ** @param [w] doc [AjPDomDocument] document
2483 ** @param [r] data [const AjPStr] text
2484 ** @return [AjPDomText] text node
2485 **
2486 ** @release 4.0.0
2487 ** @@
2488 ******************************************************************************/
2489 
ajDomDocumentCreateTextNode(AjPDomDocument doc,const AjPStr data)2490 AjPDomText ajDomDocumentCreateTextNode(AjPDomDocument doc, const AjPStr data)
2491 {
2492     if(!data)
2493         return NULL;
2494 
2495     return ajDomDocumentCreateTextNodeC(doc, MAJSTRGETPTR(data));
2496 }
2497 
2498 
2499 
2500 
2501 /* @func ajDomDocumentCreateTextNodeC *****************************************
2502 **
2503 ** Create a text node
2504 **
2505 ** @param [w] doc [AjPDomDocument] document
2506 ** @param [r] data [const char *] text
2507 ** @return [AjPDomText] text node
2508 **
2509 ** @release 4.0.0
2510 ** @@
2511 ******************************************************************************/
2512 
ajDomDocumentCreateTextNodeC(AjPDomDocument doc,const char * data)2513 AjPDomText ajDomDocumentCreateTextNodeC(AjPDomDocument doc, const char *data)
2514 {
2515     AjPDomText text = NULL;
2516 
2517     text = ajDomDocumentCreateNode(doc, ajEDomNodeTypeText);
2518     if(!text)
2519         return NULL;
2520 
2521     text->name = ajStrNewC("#text");
2522 
2523     text->sub.CharacterData.data = ajStrNewC(data);
2524     text->value = text->sub.CharacterData.data;
2525 
2526     text->sub.CharacterData.length = ajStrGetLen(text->value);
2527 
2528     return text;
2529 }
2530 
2531 
2532 
2533 
2534 /* @func ajDomDocumentCreateComment *******************************************
2535 **
2536 ** Create a comment node
2537 **
2538 ** @param [w] doc [AjPDomDocument] document
2539 ** @param [r] data [const AjPStr] text
2540 ** @return [AjPDomComment] comment node
2541 **
2542 ** @release 4.0.0
2543 ** @@
2544 ******************************************************************************/
2545 
ajDomDocumentCreateComment(AjPDomDocument doc,const AjPStr data)2546 AjPDomComment ajDomDocumentCreateComment(AjPDomDocument doc, const AjPStr data)
2547 {
2548     if(!data)
2549         return NULL;
2550 
2551     return ajDomDocumentCreateCommentC(doc, MAJSTRGETPTR(data));
2552 }
2553 
2554 
2555 
2556 
2557 /* @func ajDomDocumentCreateCommentC ******************************************
2558 **
2559 ** Create a comment node
2560 **
2561 ** @param [w] doc [AjPDomDocument] document
2562 ** @param [r] data [const char *] text
2563 ** @return [AjPDomComment] comment node
2564 **
2565 ** @release 4.0.0
2566 ** @@
2567 ******************************************************************************/
2568 
ajDomDocumentCreateCommentC(AjPDomDocument doc,const char * data)2569 AjPDomComment ajDomDocumentCreateCommentC(AjPDomDocument doc, const char *data)
2570 {
2571     AjPDomComment comment = NULL;
2572 
2573     comment = ajDomDocumentCreateNode(doc, ajEDomNodeTypeComment);
2574 
2575     if(!comment)
2576         return NULL;
2577 
2578     comment->name = ajStrNewC("#comment");
2579 
2580     comment->sub.CharacterData.data = ajStrNewC(data);
2581     comment->value = comment->sub.CharacterData.data;
2582 
2583     comment->sub.CharacterData.length = ajStrGetLen(comment->value);
2584 
2585     return comment;
2586 }
2587 
2588 
2589 
2590 
2591 /* @func ajDomDocumentCreateCDATASection **************************************
2592 **
2593 ** Create a CDATA section
2594 **
2595 ** @param [w] doc [AjPDomDocument] document
2596 ** @param [r] data [const AjPStr] text
2597 ** @return [AjPDomCDATASection] comment node
2598 **
2599 ** @release 4.0.0
2600 ** @@
2601 ******************************************************************************/
2602 
ajDomDocumentCreateCDATASection(AjPDomDocument doc,const AjPStr data)2603 AjPDomCDATASection ajDomDocumentCreateCDATASection(AjPDomDocument doc,
2604                                                    const AjPStr data)
2605 {
2606     if(!data)
2607         return NULL;
2608 
2609     return ajDomDocumentCreateCDATASectionC(doc, MAJSTRGETPTR(data));
2610 }
2611 
2612 
2613 
2614 
2615 /* @func ajDomDocumentCreateCDATASectionC *************************************
2616 **
2617 ** Create a CDATA section
2618 **
2619 ** @param [w] doc [AjPDomDocument] document
2620 ** @param [r] data [const char *] text
2621 ** @return [AjPDomCDATASection] comment node
2622 **
2623 ** @release 4.0.0
2624 ** @@
2625 ******************************************************************************/
2626 
ajDomDocumentCreateCDATASectionC(AjPDomDocument doc,const char * data)2627 AjPDomCDATASection ajDomDocumentCreateCDATASectionC(AjPDomDocument doc,
2628                                                     const char *data)
2629 {
2630     AjPDomCDATASection cdata = NULL;
2631 
2632     cdata = ajDomDocumentCreateNode(doc, ajEDomNodeTypeCdataSection);
2633 
2634     if(!cdata)
2635         return NULL;
2636 
2637     cdata->name = ajStrNewC("#cdata-section");
2638 
2639     cdata->sub.CharacterData.data = ajStrNewC(data);
2640     cdata->value = cdata->sub.CharacterData.data;
2641 
2642     cdata->sub.CharacterData.length = ajStrGetLen(cdata->value);
2643 
2644     return cdata;
2645 }
2646 
2647 
2648 
2649 
2650 /* @func ajDomDocumentCreateAttribute *****************************************
2651 **
2652 ** Create an attribute node
2653 **
2654 ** @param [w] doc [AjPDomDocument] document
2655 ** @param [r] name [const AjPStr] text
2656 ** @return [AjPDomAttr] attribute node
2657 **
2658 ** @release 4.0.0
2659 ** @@
2660 ******************************************************************************/
2661 
ajDomDocumentCreateAttribute(AjPDomDocument doc,const AjPStr name)2662 AjPDomAttr ajDomDocumentCreateAttribute(AjPDomDocument doc,
2663                                         const AjPStr name)
2664 {
2665     AjPDomAttr attr = NULL;
2666 
2667     attr = ajDomDocumentCreateNode(doc, ajEDomNodeTypeAttribute);
2668 
2669     if(!attr)
2670         return NULL;
2671 
2672     attr->sub.Attr.name = ajStrNewS(name);
2673     attr->name = attr->sub.Attr.name;
2674 
2675     attr->sub.Attr.value = ajStrNewC("");
2676     attr->value = attr->sub.Attr.value;
2677     attr->sub.Attr.specified = 1;
2678 
2679     return attr;
2680 }
2681 
2682 
2683 
2684 
2685 /* @func ajDomDocumentCreateAttributeC ****************************************
2686 **
2687 ** Create an attribute node
2688 **
2689 ** @param [w] doc [AjPDomDocument] document
2690 ** @param [r] name [const char *] text
2691 ** @return [AjPDomAttr] attribute node
2692 **
2693 ** @release 4.0.0
2694 ** @@
2695 ******************************************************************************/
2696 
ajDomDocumentCreateAttributeC(AjPDomDocument doc,const char * name)2697 AjPDomAttr ajDomDocumentCreateAttributeC(AjPDomDocument doc,
2698                                          const char *name)
2699 {
2700     AjPDomAttr attr = NULL;
2701 
2702     attr = ajDomDocumentCreateNode(doc, ajEDomNodeTypeAttribute);
2703 
2704     if(!attr)
2705         return NULL;
2706 
2707     attr->sub.Attr.name = ajStrNewC(name);
2708     attr->name = attr->sub.Attr.name;
2709 
2710     attr->sub.Attr.value = ajStrNewC("");
2711     attr->value = attr->sub.Attr.value;
2712     attr->sub.Attr.specified = 1;
2713 
2714     return attr;
2715 }
2716 
2717 
2718 
2719 
2720 /* @func ajDomDocumentCreateEntityReference ***********************************
2721 **
2722 ** Create an entity reference node
2723 **
2724 ** @param [w] doc [AjPDomDocument] document
2725 ** @param [r] name [const AjPStr] text
2726 ** @return [AjPDomEntityReference] entity reference node
2727 **
2728 ** @release 4.0.0
2729 ** @@
2730 ******************************************************************************/
2731 
ajDomDocumentCreateEntityReference(AjPDomDocument doc,const AjPStr name)2732 AjPDomEntityReference ajDomDocumentCreateEntityReference(AjPDomDocument doc,
2733                                                          const AjPStr name)
2734 {
2735     if(!name)
2736         return NULL;
2737 
2738     return ajDomDocumentCreateEntityReferenceC(doc, MAJSTRGETPTR(name));
2739 }
2740 
2741 
2742 
2743 
2744 /* @func ajDomDocumentCreateEntityReferenceC **********************************
2745 **
2746 ** Create an entity reference node
2747 **
2748 ** @param [w] doc [AjPDomDocument] document
2749 ** @param [r] name [const char *] text
2750 ** @return [AjPDomEntityReference] entity reference node
2751 **
2752 ** @release 4.0.0
2753 ** @@
2754 ******************************************************************************/
2755 
ajDomDocumentCreateEntityReferenceC(AjPDomDocument doc,const char * name)2756 AjPDomEntityReference ajDomDocumentCreateEntityReferenceC(AjPDomDocument doc,
2757                                                           const char *name)
2758 {
2759     AjPDomEntityReference eref = NULL;
2760 
2761     eref = ajDomDocumentCreateNode(doc, ajEDomNodeTypeEntityReference);
2762 
2763     if(!eref)
2764         return NULL;
2765 
2766     eref->name = ajStrNewC(name);
2767 
2768     return eref;
2769 }
2770 
2771 
2772 
2773 
2774 /* @func ajDomDocumentCreateProcessingInstruction *****************************
2775 **
2776 ** Create a processing instruction node
2777 **
2778 ** @param [w] doc [AjPDomDocument] document
2779 ** @param [r] target [const AjPStr] target
2780 ** @param [r] data [const AjPStr] data
2781 ** @return [AjPDomPi] entity reference node
2782 **
2783 ** @release 4.0.0
2784 ** @@
2785 ******************************************************************************/
2786 
ajDomDocumentCreateProcessingInstruction(AjPDomDocument doc,const AjPStr target,const AjPStr data)2787 AjPDomPi ajDomDocumentCreateProcessingInstruction(AjPDomDocument doc,
2788                                                   const AjPStr target,
2789                                                   const AjPStr data)
2790 {
2791     if(!target || !data)
2792         return NULL;
2793 
2794     return ajDomDocumentCreateProcessingInstructionC(doc, MAJSTRGETPTR(target),
2795                                                      MAJSTRGETPTR(data));
2796 }
2797 
2798 
2799 
2800 
2801 /* @func ajDomDocumentCreateProcessingInstructionC ****************************
2802 **
2803 ** Create a processing instruction node
2804 **
2805 ** @param [w] doc [AjPDomDocument] document
2806 ** @param [r] target [const char *] target
2807 ** @param [r] data [const char *] data
2808 ** @return [AjPDomPi] entity reference node
2809 **
2810 ** @release 4.0.0
2811 ** @@
2812 ******************************************************************************/
2813 
ajDomDocumentCreateProcessingInstructionC(AjPDomDocument doc,const char * target,const char * data)2814 AjPDomPi ajDomDocumentCreateProcessingInstructionC(AjPDomDocument doc,
2815                                                    const char *target,
2816                                                    const char *data)
2817 {
2818     AjPDomPi pin = NULL;
2819 
2820     pin = ajDomDocumentCreateNode(doc, ajEDomNodeTypeProcessingInstruction);
2821 
2822     if(pin)
2823     {
2824         pin->sub.ProcessingInstruction.target = ajStrNewC(target);
2825         /* AJB: dirty */
2826         pin->name = pin->sub.ProcessingInstruction.target;
2827         pin->sub.ProcessingInstruction.data = ajStrNewC(data);
2828         /* AJB: dirty */
2829         pin->value = pin->sub.ProcessingInstruction.data;
2830     }
2831 
2832     return pin;
2833 }
2834 
2835 
2836 
2837 
2838 /* @func ajDomDocumentGetElementsByTagName ************************************
2839 **
2840 ** Perform a pre-order traversal of the entire document. Return a node list
2841 ** of the elements matching tagname in the order in which they are found.
2842 **
2843 ** @param [u] doc [AjPDomDocument] document
2844 ** @param [r] name [const AjPStr] name
2845 ** @return [AjPDomNodeList] node list
2846 **
2847 ** @release 4.0.0
2848 ** @@
2849 ******************************************************************************/
2850 
ajDomDocumentGetElementsByTagName(AjPDomDocument doc,const AjPStr name)2851 AjPDomNodeList ajDomDocumentGetElementsByTagName(AjPDomDocument doc,
2852                                                  const AjPStr name)
2853 {
2854     AjPDomNodeList list = NULL;
2855 
2856     if(doc && doc->type == ajEDomNodeTypeDocumentNode && name &&
2857        (list = ajDomCreateNodeList(doc)))
2858     {
2859         domTraverse(list, doc->sub.Document.documentelement, name);
2860         return list;
2861     }
2862 
2863     return NULL;
2864 }
2865 
2866 
2867 
2868 
2869 /* @func ajDomDocumentGetElementsByTagNameC ***********************************
2870 **
2871 ** Perform a pre-order traversal of the entire document. Return a node list
2872 ** of the elements matching tagname in the order in which they are found.
2873 **
2874 ** @param [u] doc [AjPDomDocument] document
2875 ** @param [r] name [const char *] name
2876 ** @return [AjPDomNodeList] node list
2877 **
2878 ** @release 4.0.0
2879 ** @@
2880 ******************************************************************************/
2881 
ajDomDocumentGetElementsByTagNameC(AjPDomDocument doc,const char * name)2882 AjPDomNodeList ajDomDocumentGetElementsByTagNameC(AjPDomDocument doc,
2883                                                   const char *name)
2884 {
2885     AjPDomNodeList list = NULL;
2886 
2887     if(doc && doc->type == ajEDomNodeTypeDocumentNode && name &&
2888        (list = ajDomCreateNodeList(doc)))
2889     {
2890         domTraverseC(list, doc->sub.Document.documentelement, name);
2891 
2892         return list;
2893     }
2894 
2895     return NULL;
2896 }
2897 
2898 
2899 
2900 
2901 /* @func ajDomDocumentGetDoctype **********************************************
2902 **
2903 ** Get document type
2904 **
2905 ** @param [r] doc [const AjPDomDocument] document
2906 ** @return [AjPDomDocumentType] doctype
2907 **
2908 ** @release 4.0.0
2909 ** @@
2910 ******************************************************************************/
2911 
ajDomDocumentGetDoctype(const AjPDomDocument doc)2912 AjPDomDocumentType ajDomDocumentGetDoctype(const AjPDomDocument doc)
2913 {
2914     return doc ? doc->sub.Document.doctype : NULL;
2915 }
2916 
2917 
2918 
2919 
2920 /* @func ajDomDocumentGetDocumentElement **************************************
2921 **
2922 ** Get the root element of the document tree. The root element is also
2923 ** accessible through the child nodes node list member, however the children
2924 ** of a document may also be processing instructions, document type nodes,
2925 ** and comments which may precede the document element in the list.
2926 **
2927 ** @param [r] doc [const AjPDomDocument] document
2928 ** @return [AjPDomElement] root element
2929 **
2930 ** @release 4.0.0
2931 ** @@
2932 ******************************************************************************/
2933 
ajDomDocumentGetDocumentElement(const AjPDomDocument doc)2934 AjPDomElement ajDomDocumentGetDocumentElement(const AjPDomDocument doc)
2935 {
2936     return doc ? doc->sub.Document.documentelement : NULL;
2937 }
2938 
2939 
2940 
2941 
2942 /* @func ajDomPrintNode *******************************************************
2943 **
2944 ** Print a node with indentation
2945 **
2946 ** @param [r] node [const AjPDomNode] node
2947 ** @param [r] indent [ajint] indentation
2948 ** @return [void]
2949 **
2950 ** @release 4.0.0
2951 ** @@
2952 ******************************************************************************/
2953 
ajDomPrintNode(const AjPDomNode node,ajint indent)2954 void ajDomPrintNode(const AjPDomNode node, ajint indent)
2955 {
2956     AjPDomNode n;
2957     ajint i;
2958 
2959     if(!node)
2960     {
2961         ajFmtPrint("Node was null\n");
2962         return;
2963     }
2964 
2965     for(i = 0; i < indent; ++i)
2966         ajFmtPrint("    ");
2967 
2968     ajFmtPrint("%s: %S=%S\n",
2969                domKNodeinfo[node->type], node->name, node->value);
2970 
2971     if(node->type == ajEDomNodeTypeElement && node->attributes->length)
2972     {
2973         ajFmtPrint("    ");
2974         n = ajDomNodeMapItem(node->attributes, 0);
2975         ajFmtPrint("%S=%S", n->name, n->value);
2976 
2977         for(i = 1; i < node->attributes->length; ++i)
2978         {
2979             n = ajDomNodeMapItem(node->attributes, i);
2980             ajFmtPrint(",%S=%S", n->name, n->value);
2981         }
2982         ajFmtPrint("\n");
2983 
2984         for(i = 0; i < indent; ++i)
2985             ajFmtPrint("    ");
2986     }
2987 
2988     for(n = node->firstchild; n; n = n->nextsibling)
2989         ajDomPrintNode(n, indent + 1);
2990 
2991     return;
2992 }
2993 
2994 
2995 
2996 
2997 /* @func ajDomPrintNode2 ******************************************************
2998 **
2999 ** Print a node with zero indentation
3000 **
3001 ** @param [r] node [const AjPDomNode] node
3002 ** @return [void]
3003 **
3004 ** @release 4.0.0
3005 ** @@
3006 ******************************************************************************/
3007 
ajDomPrintNode2(const AjPDomNode node)3008 void ajDomPrintNode2(const AjPDomNode node)
3009 {
3010     ajFmtPrint("\n");
3011     ajDomPrintNode(node, 0);
3012 
3013     return;
3014 }
3015 
3016 
3017 
3018 
3019 /* @func ajDomNodePrintNode ***************************************************
3020 **
3021 ** Print a node internals
3022 **
3023 ** @param [r] node [const AjPDomNode] node
3024 ** @return [void]
3025 **
3026 ** @release 4.0.0
3027 ** @@
3028 ******************************************************************************/
3029 
ajDomNodePrintNode(const AjPDomNode node)3030 void ajDomNodePrintNode(const AjPDomNode node)
3031 {
3032     if(!node)
3033     {
3034         ajFmtPrint("Node was null\n");
3035 
3036         return;
3037     }
3038 
3039     ajFmtPrint("\nName=%S,Value=%S,", node->name, node->value);
3040     ajFmtPrint("\n\tType=%d", node->type);
3041 
3042     ajFmtPrint(",Parentnode->name=%S,Firstchild->name=%S",
3043                node->parentnode->name, node->firstchild->name);
3044 
3045     ajFmtPrint(",Lastchild->name=%S,\n\tChildnodes->length=%d",
3046                node->lastchild->name,
3047                (!node->childnodes ? 0 : node->childnodes->length));
3048 
3049     ajFmtPrint(",Previoussibling->name=%S,Nextsibling->name=%S,"
3050                "Attributes->length=%d\n",
3051                node->previoussibling->name, node->nextsibling->name,
3052                (!node->attributes ? 0 : node->attributes->length));
3053 
3054     return;
3055 }
3056 
3057 
3058 
3059 
3060 /* @func ajDomNodeInsertBefore ************************************************
3061 **
3062 ** Insert newchild into node directly before the existing child refchild.
3063 ** If refchild is a null pointer, newchild will be appended to the list.
3064 ** If newchild is a fragment node, all children are moved into this node
3065 ** in the same order before refchild.
3066 ** If newchild is already in the list it will first be removed.
3067 **
3068 ** @param [w] node [AjPDomNode] node
3069 ** @param [u] newchild [AjPDomNode] node to insert
3070 ** @param [u] refchild [AjPDomNode] node to insert before
3071 ** @return [AjPDomNode] inserted node
3072 **
3073 ** @release 4.0.0
3074 ** @@
3075 ******************************************************************************/
3076 
ajDomNodeInsertBefore(AjPDomNode node,AjPDomNode newchild,AjPDomNode refchild)3077 AjPDomNode ajDomNodeInsertBefore(AjPDomNode node, AjPDomNode newchild,
3078                                  AjPDomNode refchild)
3079 {
3080     AjPDomNode n = NULL;
3081     AjPDomNode nxt = NULL;
3082 
3083     if(!node || !newchild)
3084         return NULL;
3085 
3086     if(newchild->ownerdocument != node->ownerdocument &&
3087        newchild->ownerdocument != node)
3088     {
3089         ajDebug("ajDomNodeInsertBefore: Wrong document\n");
3090 
3091         return NULL;
3092     }
3093 
3094     if(refchild && refchild->parentnode != node)
3095     {
3096         ajDebug("ajDomNodeInsertBefore: Hierarchy error\n");
3097 
3098         return NULL;
3099     }
3100 
3101     if(newchild->type == ajEDomNodeTypeDocumentFragment)
3102     {
3103         for(n = newchild->firstchild; n; n = n->nextsibling)
3104             if(AJDOM_CANTDO(node, n) || domIsAncestor(n, node))
3105             {
3106                 ajDebug("ajDomNodeInsertBefore: Hierarchy Request Error\n");
3107 
3108                 return NULL;
3109             }
3110 
3111         for(n = newchild->firstchild; n; n = nxt)
3112         {
3113             nxt = n->nextsibling;
3114             if(!domDoRemoveChild(newchild, n))
3115                 return NULL;
3116 
3117             if(!ajDomNodeInsertBefore(node, n, refchild))
3118             {
3119                 ajDomDocumentDestroyNode(n->ownerdocument, &n);
3120                 return NULL;
3121             }
3122         }
3123 
3124         return newchild;
3125     }
3126 
3127 
3128     domDoRemoveChild(node, newchild);
3129 
3130     if(!(ajDomNodeListInsert(node->childnodes, newchild, refchild)))
3131         return NULL;
3132 
3133     if(!node->firstchild)
3134     {
3135         node->firstchild = newchild;
3136         node->lastchild  = newchild;
3137         newchild->previoussibling = NULL;
3138         newchild->nextsibling = NULL;
3139     }
3140     else if(!refchild)
3141     {
3142         newchild->previoussibling = node->lastchild;
3143         node->lastchild->nextsibling = newchild;
3144         node->lastchild = newchild;
3145         newchild->nextsibling = NULL;
3146     }
3147     else
3148     {
3149         newchild->previoussibling = refchild->previoussibling;
3150         newchild->nextsibling = refchild;
3151         if(refchild == node->firstchild)
3152         {
3153             node->firstchild = newchild;
3154             newchild->previoussibling = NULL;
3155         }
3156         else
3157             refchild->previoussibling->nextsibling = newchild;
3158 
3159         refchild->previoussibling = newchild;
3160     }
3161 
3162     newchild->parentnode = node;
3163 
3164     if(AJDOM_DOCMOD(node, newchild))
3165         node->sub.Document.documentelement = newchild;
3166     else if(AJDOM_DOCTYPEMOD(node, newchild))
3167     {
3168         node->sub.Document.doctype = newchild;
3169         newchild->ownerdocument = node;
3170     }
3171 
3172     domUpdateNode(node);
3173 
3174     return newchild;
3175 }
3176 
3177 
3178 
3179 
3180 /* @func ajDomNodeListInsert **************************************************
3181 **
3182 ** Insert newchild into list directly before the existing child refchild.
3183 **
3184 ** @param [u] list [AjPDomNodeList] list
3185 ** @param [u] newchild [AjPDomNode] node to insert
3186 ** @param [u] refchild [AjPDomNode] node to insert before
3187 ** @return [AjPDomNodeEntry] inserted node
3188 **
3189 ** @release 4.0.0
3190 ** @@
3191 ******************************************************************************/
3192 
ajDomNodeListInsert(AjPDomNodeList list,AjPDomNode newchild,AjPDomNode refchild)3193 AjPDomNodeEntry ajDomNodeListInsert(AjPDomNodeList list, AjPDomNode newchild,
3194                                     AjPDomNode refchild)
3195 {
3196     AjPDomNodeEntry e = NULL;
3197     AjPDomNodeEntry s = NULL;
3198 
3199     if(!list)
3200         return NULL;
3201 
3202     if(list->filter)
3203     {
3204         ajDebug("ajDomNodeListInsert: Filtered list error\n");
3205 
3206         return NULL;
3207     }
3208 
3209     if(refchild)
3210     {
3211         s = domDoLookupNode(list, refchild);
3212         if(!s || s->node != refchild)
3213         {
3214             ajDebug("ajDomNodeListInsert: not found error\n");
3215 
3216             return NULL;
3217         }
3218     }
3219 
3220     AJNEW0(e);
3221 
3222     domAddToMap(list, newchild, e);
3223 
3224     e->node = newchild;
3225     if(!list->length)
3226     {
3227         list->first = e;
3228         list->last = e;
3229     }
3230     else if(!refchild)
3231     {
3232         e->prev = list->last;
3233         list->last->next = e;
3234         list->last = e;
3235     }
3236     else
3237     {
3238         e->prev = s->prev;
3239         e->next = s;
3240         if(s == list->first)
3241             list->first = e;
3242         else
3243             s->prev->next = e;
3244         s->prev = e;
3245     }
3246 
3247     ++list->length;
3248 
3249     if(newchild->type == ajEDomNodeTypeAttribute)
3250         newchild->sub.Attr.ownerelement = list->ownerelement;
3251 
3252     return e;
3253 }
3254 
3255 
3256 
3257 
3258 /* @func ajDomNodeReplaceChild ************************************************
3259 **
3260 ** Replace oldchild with newchild in the list of children.
3261 **
3262 ** @param [u] node [AjPDomNode] node
3263 ** @param [u] newchild [AjPDomNode] node to insert
3264 ** @param [u] oldchild [AjPDomNode] node to replace
3265 ** @return [AjPDomNode] oldchild node
3266 **
3267 ** @release 4.0.0
3268 ** @@
3269 ******************************************************************************/
3270 
ajDomNodeReplaceChild(AjPDomNode node,AjPDomNode newchild,AjPDomNode oldchild)3271 AjPDomNode ajDomNodeReplaceChild(AjPDomNode node, AjPDomNode newchild,
3272                                  AjPDomNode oldchild)
3273 {
3274     AjPDomNode nxt = NULL;
3275     AjPDomNode n   = NULL;
3276 
3277     if(!node || !newchild || !oldchild)
3278         return NULL;
3279 
3280     if(newchild->ownerdocument != node->ownerdocument &&
3281        newchild->ownerdocument != node)
3282     {
3283         ajDebug("ajDomNodeReplaceChild: Wrong document\n");
3284 
3285         return NULL;
3286     }
3287 
3288     if(!ajDomNodeListExists(node->childnodes, oldchild))
3289     {
3290         ajDebug("ajDomNodeReplaceChild: Oldchild not found\n");
3291 
3292         return NULL;
3293     }
3294 
3295     if(newchild->type == ajEDomNodeTypeDocumentFragment)
3296     {
3297         for(n = newchild->firstchild; n; n = n->nextsibling)
3298             if(AJDOM_CANTDO(node, n) || domIsAncestor(n, node))
3299             {
3300                 ajDebug("ajDomNodeReplaceChild: Hierarchy Request Error\n");
3301 
3302                 return NULL;
3303             }
3304 
3305         for(n = newchild->firstchild; n; n = nxt)
3306         {
3307             nxt = n->nextsibling;
3308             if(!domDoRemoveChild(newchild, n))
3309                 return NULL;
3310 
3311             if(!ajDomNodeInsertBefore(node, n, oldchild))
3312             {
3313                 ajDomDocumentDestroyNode(n->ownerdocument, &n);
3314 
3315                 return NULL;
3316             }
3317         }
3318 
3319         if(!domDoRemoveChild(node, oldchild))
3320             return NULL;
3321 
3322         return oldchild;
3323     }
3324 
3325 
3326     if(AJDOM_CANTDO(node, newchild) || domIsAncestor(newchild, node))
3327     {
3328         ajDebug("ajDomNodeReplaceChild: Hierarchy Request Error\n");
3329         return NULL;
3330     }
3331 
3332     domDoRemoveChild(node, newchild);
3333 
3334     if(!ajDomNodeListExists(node->childnodes, oldchild))
3335         return NULL;
3336 
3337     ajDomNodeListReplace(node->childnodes, newchild, oldchild);
3338 
3339     node->firstchild = node->childnodes->first->node;
3340     node->lastchild  = node->childnodes->last->node;
3341 
3342     if((newchild->previoussibling = oldchild->previoussibling))
3343         newchild->previoussibling->nextsibling = newchild;
3344 
3345     if((newchild->nextsibling = oldchild->nextsibling))
3346         newchild->nextsibling->previoussibling = newchild;
3347 
3348     newchild->parentnode = node;
3349     oldchild->parentnode = NULL;
3350     oldchild->previoussibling = NULL;
3351     oldchild->nextsibling = NULL;
3352 
3353     if(AJDOM_DOCMOD(node, newchild))
3354         node->sub.Document.documentelement = newchild;
3355     else if(AJDOM_DOCTYPEMOD(node, newchild))
3356     {
3357         node->sub.Document.doctype = newchild;
3358         newchild->ownerdocument = node;
3359     }
3360 
3361     domUpdateNode(node);
3362 
3363     return oldchild;
3364 }
3365 
3366 
3367 
3368 
3369 /* @func ajDomNodeListReplace *************************************************
3370 **
3371 ** Replace oldchild with newchild in a node list.
3372 **
3373 ** @param [u] list [AjPDomNodeList] list
3374 ** @param [u] newchild [AjPDomNode] node to insert
3375 ** @param [u] oldchild [AjPDomNode] node to replace
3376 ** @return [AjPDomNodeEntry] inserted node
3377 **
3378 ** @release 4.0.0
3379 ** @@
3380 ******************************************************************************/
3381 
ajDomNodeListReplace(AjPDomNodeList list,AjPDomNode newchild,AjPDomNode oldchild)3382 AjPDomNodeEntry ajDomNodeListReplace(AjPDomNodeList list, AjPDomNode newchild,
3383                                      AjPDomNode oldchild)
3384 {
3385     AjPDomNodeEntry e = NULL;
3386 
3387     if(!list)
3388         return NULL;
3389 
3390     if(list->filter)
3391     {
3392         ajDebug("ajDomNodeListReplace: Filtered list error\n");
3393 
3394         return NULL;
3395     }
3396 
3397     e = domDoLookupNode(list, oldchild);
3398     if(!e)
3399     {
3400         ajDebug("ajDomNodeListReplace: Not found error\n");
3401 
3402         return NULL;
3403     }
3404 
3405     domRemoveFromMap(list, oldchild);
3406     domAddToMap(list, newchild, e);
3407 
3408     e->node = newchild;
3409 
3410     if(oldchild->type == ajEDomNodeTypeAttribute)
3411         oldchild->sub.Attr.ownerelement = NULL;
3412 
3413     return e;
3414 }
3415 
3416 
3417 
3418 
3419 /* @funcstatic domNodeCloneNode ***********************************************
3420 **
3421 ** Low level clone node
3422 **
3423 ** @param [u] ownerdocument [AjPDomDocument] owner document for clone
3424 ** @param [r] node [const AjPDomNode] node to clone
3425 ** @param [r] deep [AjBool] do a deep clone
3426 ** @return [AjPDomNode] clone
3427 **
3428 ** @release 4.0.0
3429 ** @@
3430 ******************************************************************************/
3431 
domNodeCloneNode(AjPDomDocument ownerdocument,const AjPDomNode node,AjBool deep)3432 static AjPDomNode domNodeCloneNode(AjPDomDocument ownerdocument,
3433                                    const AjPDomNode node, AjBool deep)
3434 {
3435     AjPDomNode clone  = NULL;
3436     AjPDomNode ntmp   = NULL;
3437     AjPDomNode ctmp   = NULL;
3438     AjPDomNodeEntry e = NULL;
3439 
3440 
3441     switch(node->type)
3442     {
3443         case ajEDomNodeTypeElement:
3444             clone = ajDomDocumentCreateElement(ownerdocument, node->name);
3445 
3446             if(clone)
3447                 for(e = node->attributes->first; e; e = e->next)
3448                     if(!(ctmp = domNodeCloneNode(ownerdocument, e->node, deep))
3449                        ||
3450                        !ajDomNodeListAppend(clone->attributes, ctmp))
3451                     {
3452                         ajDomDocumentDestroyNode(clone->ownerdocument, &ctmp);
3453                         ajDomDocumentDestroyNode(clone->ownerdocument, &clone);
3454                         return NULL;
3455                     }
3456             break;
3457 
3458         case ajEDomNodeTypeAttribute:
3459             if((clone = ajDomDocumentCreateAttribute(ownerdocument,
3460                                                      node->name)))
3461             {
3462                 clone->sub.Attr.specified = node->sub.Attr.specified;
3463 
3464                 if(!node->value)
3465                     return NULL;
3466 
3467                 ajStrAssignS(&clone->value, node->value);
3468                 /* AJB: dirty */
3469                 clone->sub.Attr.value = clone->value;
3470             }
3471 
3472             break;
3473 
3474         case ajEDomNodeTypeComment:
3475             clone = ajDomDocumentCreateComment(ownerdocument, node->value);
3476             break;
3477 
3478         case ajEDomNodeTypeText:
3479             clone = ajDomDocumentCreateTextNode(ownerdocument, node->value);
3480             break;
3481 
3482         case ajEDomNodeTypeCdataSection:
3483             clone = ajDomDocumentCreateCDATASection(ownerdocument,
3484                                                     node->value);
3485             break;
3486 
3487         case ajEDomNodeTypeDocumentFragment:
3488             clone = ajDomDocumentCreateDocumentFragment(ownerdocument);
3489             break;
3490 
3491         case ajEDomNodeTypeDocumentNode:
3492             clone = ownerdocument;
3493             break;
3494 
3495         case ajEDomNodeTypeProcessingInstruction:
3496             clone = ajDomDocumentCreateProcessingInstruction(
3497                 ownerdocument,
3498                 node->sub.ProcessingInstruction.target,
3499                 node->sub.ProcessingInstruction.data);
3500             break;
3501 
3502         case ajEDomNodeTypeEntityNode:
3503             if((clone = ajDomDocumentCreateNode(ownerdocument,
3504                                                 ajEDomNodeTypeEntityNode)))
3505             {
3506                 clone->name  = ajStrNewS(node->name);
3507                 clone->value = ajStrNewS(node->value);
3508 
3509                 if(node->sub.Entity.publicid)
3510                     clone->sub.Entity.publicid = ajStrNewS(node->sub.Entity.
3511                                                            publicid);
3512                 if(node->sub.Entity.systemid)
3513                     clone->sub.Entity.systemid = ajStrNewS(node->sub.Entity.
3514                                                            systemid);
3515                 if(node->sub.Entity.notationname)
3516                     clone->sub.Entity.notationname = ajStrNewS(node->sub.
3517                                                                Entity.
3518                                                                notationname);
3519             }
3520 
3521             break;
3522 
3523         case ajEDomNodeTypeNotation:
3524             if((clone = ajDomDocumentCreateNode(ownerdocument,
3525                                                 ajEDomNodeTypeNotation)))
3526             {
3527                 clone->name = ajStrNewS(node->name);
3528 
3529                 if(node->sub.Notation.publicid)
3530                     clone->sub.Notation.publicid = ajStrNewS(node->sub.
3531                                                              Notation.
3532                                                              publicid);
3533                 if(node->sub.Notation.systemid)
3534                     clone->sub.Notation.systemid = ajStrNewS(node->sub.
3535                                                              Notation.
3536                                                              systemid);
3537             }
3538 
3539             break;
3540 
3541         case ajEDomNodeTypeDocumentType:
3542             if((clone = ajDomImplementationCreateDocumentType(node->name,
3543                                                               NULL, NULL)))
3544             {
3545                 if(node->sub.DocumentType.publicid)
3546                     clone->sub.DocumentType.publicid = ajStrNewS(node->sub.
3547                                                                  DocumentType.
3548                                                                  publicid);
3549                 if(node->sub.DocumentType.systemid)
3550                     clone->sub.DocumentType.systemid = ajStrNewS(node->sub.
3551                                                                  DocumentType.
3552                                                                  systemid);
3553             }
3554 
3555             ownerdocument->sub.Document.doctype = clone;
3556             clone->ownerdocument = ownerdocument;
3557             break;
3558 
3559         case ajEDomNodeTypeEntityReference:
3560             ajDebug("Entity reference clone not implemented\n");
3561             return NULL;
3562 
3563         default:
3564             ajDebug("domNodeCloneNode got unexpected "
3565                     "AJAX DOM Node type %d.\n", node->type);
3566     }
3567 
3568 
3569     if(deep && clone && node->childnodes)
3570         for(ntmp = node->firstchild; ntmp; ntmp = ntmp->nextsibling)
3571         {
3572             ctmp = domNodeCloneNode(ownerdocument, ntmp, ajTrue);
3573 
3574             if(!ctmp || !ajDomNodeAppendChild(clone, ctmp))
3575             {
3576                 ajDomDocumentDestroyNode(clone->ownerdocument, &ctmp);
3577                 ajDomDocumentDestroyNode(clone->ownerdocument, &clone);
3578                 return NULL;
3579             }
3580         }
3581 
3582     return clone;
3583 }
3584 
3585 
3586 
3587 
3588 /* @func ajDomNodeCloneNode ***************************************************
3589 **
3590 ** Clone node
3591 **
3592 ** @param [u] node [AjPDomNode] node to clone
3593 ** @param [r] deep [AjBool] do a deep clone
3594 ** @return [AjPDomNode] clone
3595 **
3596 ** @release 4.0.0
3597 ** @@
3598 ******************************************************************************/
3599 
ajDomNodeCloneNode(AjPDomNode node,AjBool deep)3600 AjPDomNode ajDomNodeCloneNode(AjPDomNode node, AjBool deep)
3601 {
3602     AjPDomDocument doc = NULL;
3603 
3604     if(!node)
3605         return NULL;
3606 
3607     if(node->type == ajEDomNodeTypeDocumentNode)
3608     {
3609         if(!(doc = ajDomImplementationCreateDocument(NULL, NULL, NULL)))
3610             return NULL;
3611 
3612         return domNodeCloneNode(doc, node, deep);
3613     }
3614 
3615     return domNodeCloneNode(node->ownerdocument, node, deep);
3616 }
3617 
3618 
3619 
3620 
3621 /* @funcstatic domWriteEncoded ************************************************
3622 **
3623 ** Write a string using entity substitutions
3624 **
3625 ** @param [r] s [const AjPStr] string
3626 ** @param [w] outf [AjPFile] output file
3627 ** @return [void]
3628 **
3629 ** @release 4.0.0
3630 ** @@
3631 ******************************************************************************/
3632 
domWriteEncoded(const AjPStr s,AjPFile outf)3633 static void domWriteEncoded(const AjPStr s, AjPFile outf)
3634 {
3635     size_t len;
3636     const char *p;
3637     AjPStr tmp = NULL;
3638 
3639     tmp = ajStrNew();
3640 
3641     p = ajStrGetPtr(s);
3642 
3643     while(*p)
3644     {
3645         len = strcspn(p, "<>&\"");
3646         if(len)
3647         {
3648             ajStrAssignSubC(&tmp, p, 0, len - 1);
3649             ajFmtPrintF(outf, "%S", tmp);
3650             p += len;
3651         }
3652 
3653         switch (*p)
3654         {
3655             case '\0':
3656                 break;
3657             case '<':
3658                 ajFmtPrintF(outf, "&lt;");
3659                 break;
3660             case '>':
3661                 ajFmtPrintF(outf, "&gt;");
3662                 break;
3663             case '&':
3664                 ajFmtPrintF(outf, "&apos;");
3665                 break;
3666             case '"':
3667                 ajFmtPrintF(outf, "&quot;");
3668                 break;
3669             default:
3670                 break;
3671         };
3672 
3673         if(*p)
3674             ++p;
3675     }
3676 
3677     ajStrDel(&tmp);
3678 
3679     return;
3680 }
3681 
3682 
3683 
3684 
3685 /* @funcstatic domWriteEncodedIndent ******************************************
3686 **
3687 ** Write a string using entity substitutions, indenting at each internal newline
3688 **
3689 ** @param [r] s [const AjPStr] string
3690 ** @param [w] outf [AjPFile] output file
3691 ** @param [r] indent [ajuint] indentation
3692 ** @return [void]
3693 **
3694 ** @release 4.0.0
3695 ** @@
3696 ******************************************************************************/
3697 
domWriteEncodedIndent(const AjPStr s,AjPFile outf,ajuint indent)3698 static void domWriteEncodedIndent(const AjPStr s, AjPFile outf, ajuint indent)
3699 {
3700     size_t len;
3701     const char *p;
3702     char lastp = '\0';
3703     AjPStr tmp = NULL;
3704     ajuint i;
3705 
3706     tmp = ajStrNew();
3707 
3708     p = ajStrGetPtr(s);
3709 
3710     while(*p)
3711     {
3712         len = strcspn(p, "<>&\"\n\r");
3713         if(len)
3714         {
3715             ajStrAssignSubC(&tmp, p, 0, len - 1);
3716             ajFmtPrintF(outf, "%S", tmp);
3717             p += len;
3718         }
3719 
3720         switch (*p)
3721         {
3722             case '\0':
3723                 break;
3724 
3725             case '\n':
3726                 if(lastp == '\r')
3727                     break;
3728             case '\r':
3729                 ajFmtPrintF(outf, "\n");
3730                 for(i=0; i<indent;i++)
3731                     ajFmtPrintF(outf, " ");
3732                 break;
3733 
3734             case '<':
3735                 ajFmtPrintF(outf, "&lt;");
3736                 break;
3737 
3738             case '>':
3739                 ajFmtPrintF(outf, "&gt;");
3740                 break;
3741 
3742             case '&':
3743                 ajFmtPrintF(outf, "&apos;");
3744                 break;
3745 
3746             case '"':
3747                 ajFmtPrintF(outf, "&quot;");
3748                 break;
3749 
3750             default:
3751                 break;
3752         };
3753 
3754         lastp = *p;
3755 
3756         if(*p)
3757             ++p;
3758     }
3759 
3760     ajStrDel(&tmp);
3761 
3762     return;
3763 }
3764 
3765 
3766 
3767 
3768 /* @func ajDomNodeHasChildNodes ***********************************************
3769 **
3770 ** Check whether a node has any children
3771 **
3772 ** @param [r] node [const AjPDomNode] node to clone
3773 ** @return [AjBool] true if children
3774 **
3775 ** @release 4.0.0
3776 ** @@
3777 ******************************************************************************/
3778 
ajDomNodeHasChildNodes(const AjPDomNode node)3779 AjBool ajDomNodeHasChildNodes(const AjPDomNode node)
3780 {
3781     return node != NULL && node->firstchild;
3782 }
3783 
3784 
3785 
3786 
3787 /* @func ajDomWrite ***********************************************************
3788 **
3789 ** Write XML from memory
3790 **
3791 ** @param [r] node [const AjPDomDocument] document to write
3792 ** @param [u] outf [AjPFile] output file
3793 ** @return [ajint] zero OK, negative if error
3794 **
3795 ** @release 4.0.0
3796 ** @@
3797 ******************************************************************************/
3798 
ajDomWrite(const AjPDomDocument node,AjPFile outf)3799 ajint ajDomWrite(const AjPDomDocument node, AjPFile outf)
3800 {
3801     AjPDomNodeEntry e = NULL;
3802     AjPDomNode c = NULL;
3803 
3804     if(!node || !outf)
3805         return -1;
3806 
3807     switch(node->type)
3808     {
3809         case ajEDomNodeTypeElement:
3810             ajFmtPrintF(outf, "<");
3811             ajFmtPrintF(outf, "%S", node->name);
3812 
3813             for(e = node->attributes->first; e; e = e->next)
3814             {
3815                 ajFmtPrintF(outf, " %S=\"", e->node->name);
3816                 domWriteEncoded(e->node->value, outf);
3817                 ajFmtPrintF(outf, "\"");
3818             }
3819 
3820             if(ajDomNodeHasChildNodes(node))
3821             {
3822                 ajFmtPrintF(outf, ">");
3823 
3824                 for(c = node->firstchild; c; c = c->nextsibling)
3825                     if(ajDomWrite(c, outf) == -1)
3826                         return -1;
3827 
3828                 ajFmtPrintF(outf, "</");
3829                 ajFmtPrintF(outf, "%S", node->name);
3830                 ajFmtPrintF(outf, ">");
3831             }
3832             else
3833                 ajFmtPrintF(outf, "/>");
3834 
3835             break;
3836 
3837         case ajEDomNodeTypeAttribute:
3838             break;
3839 
3840         case ajEDomNodeTypeText:
3841             domWriteEncoded(node->value, outf);
3842             break;
3843 
3844         case ajEDomNodeTypeCdataSection:
3845             break;
3846 
3847         case ajEDomNodeTypeEntityReference:
3848             break;
3849 
3850         case ajEDomNodeTypeNotation:
3851             ajFmtPrintF(outf, "    <!NOTATION ");
3852             ajFmtPrintF(outf, "%S", node->name);
3853 
3854             if(node->sub.Notation.publicid)
3855             {
3856                 ajFmtPrintF(outf, " PUBLIC \"");
3857                 ajFmtPrintF(outf, "%S", node->sub.Notation.publicid);
3858                 ajFmtPrintF(outf, "\" \"");
3859                 ajFmtPrintF(outf, "%S", node->sub.Notation.systemid);
3860                 ajFmtPrintF(outf, "\"");
3861             }
3862             else if(node->sub.Notation.systemid)
3863             {
3864                 ajFmtPrintF(outf, " SYSTEM \"");
3865                 ajFmtPrintF(outf, "%S", node->sub.Notation.systemid);
3866                 ajFmtPrintF(outf, "\"");
3867             }
3868 
3869             ajFmtPrintF(outf, ">");
3870             break;
3871 
3872         case ajEDomNodeTypeEntityNode:
3873             ajFmtPrintF(outf, "    <!ENTITY ");
3874             ajFmtPrintF(outf, "%S", node->name);
3875 
3876             if(node->value)
3877             {
3878                 ajFmtPrintF(outf, "\"");
3879                 ajFmtPrintF(outf, "%S", node->value);
3880                 ajFmtPrintF(outf, "\"");
3881             }
3882             else
3883             {
3884                 if(node->sub.Entity.publicid)
3885                 {
3886                     ajFmtPrintF(outf, " PUBLIC \"");
3887                     ajFmtPrintF(outf, "%S", node->sub.Entity.publicid);
3888                     ajFmtPrintF(outf, "\" \"");
3889                     ajFmtPrintF(outf, "%S", node->sub.Entity.systemid);
3890                     ajFmtPrintF(outf, "\"");
3891                 }
3892                 else if(node->sub.Entity.systemid)
3893                 {
3894                     ajFmtPrintF(outf, " SYSTEM \"");
3895                     ajFmtPrintF(outf, "%S", node->sub.Entity.systemid);
3896                     ajFmtPrintF(outf, "\"");
3897                 }
3898 
3899                 if(node->sub.Entity.notationname)
3900                 {
3901                     ajFmtPrintF(outf, " NDATA ");
3902                     ajFmtPrintF(outf, "%S", node->sub.Entity.notationname);
3903                 }
3904             }
3905 
3906             ajFmtPrintF(outf, ">");
3907             break;
3908 
3909         case ajEDomNodeTypeProcessingInstruction:
3910             ajFmtPrintF(outf, "<?");
3911             ajFmtPrintF(outf, "%S ", node->sub.ProcessingInstruction.target);
3912             domWriteEncoded(node->sub.ProcessingInstruction.data, outf);
3913             ajFmtPrintF(outf, "?>");
3914             break;
3915 
3916         case ajEDomNodeTypeComment:
3917             ajFmtPrintF(outf, "<!--");
3918             domWriteEncoded(node->value, outf);
3919             ajFmtPrintF(outf, "-->");
3920             break;
3921 
3922         case ajEDomNodeTypeDocumentNode:
3923             ajFmtPrintF(outf, "<?xml");
3924             ajFmtPrintF(outf, " version=\"");
3925 
3926             if(node->sub.Document.version)
3927                 ajFmtPrintF(outf, "%S", node->sub.Document.version);
3928             else
3929                 ajFmtPrintF(outf, "1.0");
3930 
3931             ajFmtPrintF(outf, "\"");
3932 
3933             if(node->sub.Document.standalone)
3934                 ajFmtPrintF(outf, " standalone=\"yes\"");
3935 
3936             ajFmtPrintF(outf, "?>\n");
3937 
3938             for(c=node->firstchild; c; c=c->nextsibling)
3939                 if(ajDomWrite(c, outf) == -1)
3940                     return -1;
3941 
3942             ajFmtPrintF(outf, "\n");
3943             break;
3944 
3945         case ajEDomNodeTypeDocumentType:
3946             ajFmtPrintF(outf, "\n<!DOCTYPE ");
3947             ajFmtPrintF(outf, "%S", node->sub.DocumentType.name);
3948 
3949             if(node->sub.DocumentType.systemid)
3950             {
3951                 ajFmtPrintF(outf, " SYSTEM \"");
3952                 ajFmtPrintF(outf, "%S", node->sub.DocumentType.systemid);
3953                 ajFmtPrintF(outf, "\"");
3954             }
3955             else if(node->sub.DocumentType.publicid)
3956             {
3957                 ajFmtPrintF(outf, " PUBLIC \"");
3958                 ajFmtPrintF(outf, "%S", node->sub.DocumentType.publicid);
3959                 ajFmtPrintF(outf, "\"");
3960             }
3961 
3962             if(node->sub.DocumentType.internalsubset)
3963             {
3964                 ajFmtPrintF(outf, " [\n");
3965                 ajFmtPrintF(outf, "%S", node->sub.DocumentType.internalsubset);
3966                 ajFmtPrintF(outf, "]>\n");
3967             }
3968             else
3969                 ajFmtPrintF(outf, ">\n");
3970 
3971             break;
3972 
3973         case ajEDomNodeTypeDocumentFragment:
3974             break;
3975 
3976         default:
3977             ajDebug("ajDomWrite got unexpected "
3978                     "AJAX DOM Node type %d.\n", node->type);
3979     }
3980 
3981     return 0;
3982 }
3983 
3984 
3985 
3986 
3987 /* @func ajDomWriteIndent *****************************************************
3988 **
3989 ** Write XML from memory
3990 **
3991 ** @param [r] node [const AjPDomDocument] document to write
3992 ** @param [u] outf [AjPFile] output file
3993 ** @param [r] indent [ajint] indent level
3994 ** @return [ajint] Positive OK, zero OK but no additional output,
3995 **                 negative if error
3996 **
3997 ** @release 4.1.0
3998 ** @@
3999 ******************************************************************************/
4000 
ajDomWriteIndent(const AjPDomDocument node,AjPFile outf,ajint indent)4001 ajint ajDomWriteIndent(const AjPDomDocument node, AjPFile outf, ajint indent)
4002 {
4003     AjPDomNodeEntry e = NULL;
4004     AjPDomNode c = NULL;
4005     ajint i;
4006     AjPStr tmpstr = NULL;
4007     ajint iret = 0;
4008 
4009     if(!node || !outf)
4010         return -1;
4011 
4012     ajUser("ajDomWriteIndent indent %u type %u", indent, node->type);
4013 
4014     switch(node->type)
4015     {
4016         case ajEDomNodeTypeElement:
4017             for(i = 0; i < indent; ++i)
4018                 ajFmtPrintF(outf, " ");
4019 
4020             ajFmtPrintF(outf, "<");
4021             ajFmtPrintF(outf, "%S", node->name);
4022 
4023             for(e = node->attributes->first; e; e = e->next)
4024             {
4025                 ajFmtPrintF(outf, " %S=\"", e->node->name);
4026                 domWriteEncodedIndent(e->node->value, outf, indent);
4027                 ajFmtPrintF(outf, "\"");
4028             }
4029 
4030             if(ajDomNodeHasChildNodes(node))
4031             {
4032                 ajFmtPrintF(outf, ">\n");
4033 
4034                 for(c = node->firstchild; c; c = c->nextsibling)
4035                 {
4036                     iret = ajDomWriteIndent(c, outf, indent + 2);
4037 
4038                     if(iret < 0)
4039                         return -1;
4040                 }
4041 
4042                 if(iret)
4043                     ajFmtPrintF(outf, "\n");
4044 
4045                 for(i = 0; i < indent; ++i)
4046                     ajFmtPrintF(outf, " ");
4047 
4048                 ajFmtPrintF(outf, "</");
4049                 ajFmtPrintF(outf, "%S", node->name);
4050                 ajFmtPrintF(outf, ">\n");
4051             }
4052             else
4053             {
4054                 ajFmtPrintF(outf, "/>\n");
4055                 iret = 1;
4056             }
4057 
4058             break;
4059 
4060         case ajEDomNodeTypeAttribute:
4061             break;
4062 
4063         case ajEDomNodeTypeText:
4064             ajStrAssignS(&tmpstr, node->value);
4065             ajStrTrimWhiteStart(&tmpstr);
4066             ajStrTrimWhiteEnd(&tmpstr);
4067 
4068             if(ajStrGetLen(tmpstr))
4069             {
4070                 for(i = 0; i < indent; ++i)
4071                     ajFmtPrintF(outf, " ");
4072                 domWriteEncodedIndent(tmpstr, outf, indent);
4073                 iret = 1;
4074             }
4075 
4076             break;
4077 
4078         case ajEDomNodeTypeCdataSection:
4079             break;
4080 
4081         case ajEDomNodeTypeEntityReference:
4082             break;
4083 
4084         case ajEDomNodeTypeNotation:
4085             for(i = 0; i < indent; ++i)
4086                 ajFmtPrintF(outf, " ");
4087 
4088             ajFmtPrintF(outf, "    <!NOTATION ");
4089             ajFmtPrintF(outf, "%S", node->name);
4090 
4091             if(node->sub.Notation.publicid)
4092             {
4093                 ajFmtPrintF(outf, " PUBLIC \"");
4094                 ajFmtPrintF(outf, "%S", node->sub.Notation.publicid);
4095                 ajFmtPrintF(outf, "\" \"");
4096                 ajFmtPrintF(outf, "%S", node->sub.Notation.systemid);
4097                 ajFmtPrintF(outf, "\"");
4098             }
4099             else if(node->sub.Notation.systemid)
4100             {
4101                 ajFmtPrintF(outf, " SYSTEM \"");
4102                 ajFmtPrintF(outf, "%S", node->sub.Notation.systemid);
4103                 ajFmtPrintF(outf, "\"");
4104             }
4105 
4106             ajFmtPrintF(outf, ">\n");
4107             iret = 1;
4108             break;
4109 
4110         case ajEDomNodeTypeEntityNode:
4111             for(i = 0; i < indent; ++i)
4112                 ajFmtPrintF(outf, " ");
4113 
4114             ajFmtPrintF(outf, "    <!ENTITY ");
4115             ajFmtPrintF(outf, "%S", node->name);
4116 
4117             if(node->value)
4118             {
4119                 ajFmtPrintF(outf, "\"");
4120                 ajFmtPrintF(outf, "%S", node->value);
4121                 ajFmtPrintF(outf, "\"");
4122             }
4123             else
4124             {
4125                 if(node->sub.Entity.publicid)
4126                 {
4127                     ajFmtPrintF(outf, " PUBLIC \"");
4128                     ajFmtPrintF(outf, "%S", node->sub.Entity.publicid);
4129                     ajFmtPrintF(outf, "\" \"");
4130                     ajFmtPrintF(outf, "%S", node->sub.Entity.systemid);
4131                     ajFmtPrintF(outf, "\"");
4132                 }
4133                 else if(node->sub.Entity.systemid)
4134                 {
4135                     ajFmtPrintF(outf, " SYSTEM \"");
4136                     ajFmtPrintF(outf, "%S", node->sub.Entity.systemid);
4137                     ajFmtPrintF(outf, "\"");
4138                 }
4139 
4140                 if(node->sub.Entity.notationname)
4141                 {
4142                     ajFmtPrintF(outf, " NDATA ");
4143                     ajFmtPrintF(outf, "%S", node->sub.Entity.notationname);
4144                 }
4145             }
4146 
4147             ajFmtPrintF(outf, ">\n");
4148             iret = 1;
4149             break;
4150 
4151         case ajEDomNodeTypeProcessingInstruction:
4152             for(i = 0; i < indent; ++i)
4153                 ajFmtPrintF(outf, " ");
4154 
4155             ajFmtPrintF(outf, "<?");
4156             ajFmtPrintF(outf, "%S ", node->sub.ProcessingInstruction.target);
4157             domWriteEncodedIndent(node->sub.ProcessingInstruction.data,
4158                                   outf, indent);
4159             ajFmtPrintF(outf, "?>\n");
4160             iret = 1;
4161             break;
4162 
4163         case ajEDomNodeTypeComment:
4164             for(i = 0; i < indent; ++i)
4165                 ajFmtPrintF(outf, " ");
4166 
4167             ajFmtPrintF(outf, "<!--");
4168             domWriteEncodedIndent(node->value, outf, indent);
4169             ajFmtPrintF(outf, "-->\n");
4170             iret = 1;
4171             break;
4172 
4173         case ajEDomNodeTypeDocumentNode:
4174             for(i = 0; i < indent; ++i)
4175                 ajFmtPrintF(outf, " ");
4176 
4177             ajFmtPrintF(outf, "<?xml");
4178             ajFmtPrintF(outf, " version=\"");
4179 
4180             if(node->sub.Document.version)
4181                 ajFmtPrintF(outf, "%S", node->sub.Document.version);
4182             else
4183                 ajFmtPrintF(outf, "1.0");
4184 
4185             ajFmtPrintF(outf, "\"");
4186 
4187             if(node->sub.Document.standalone)
4188                 ajFmtPrintF(outf, " standalone=\"yes\"");
4189 
4190             ajFmtPrintF(outf, "?>\n");
4191 
4192             for(c = node->firstchild; c; c = c->nextsibling)
4193             {
4194                 iret = ajDomWriteIndent(c, outf, indent);
4195                 if(iret < 0)
4196                     return -1;
4197             }
4198 
4199             if(iret)
4200                 ajFmtPrintF(outf, "\n");
4201 
4202             iret=1;
4203             break;
4204 
4205         case ajEDomNodeTypeDocumentType:
4206             ajFmtPrintF(outf, "\n");
4207 
4208             for(i = 0; i < indent; ++i)
4209                 ajFmtPrintF(outf, " ");
4210 
4211             ajFmtPrintF(outf, "<!DOCTYPE ");
4212 
4213             ajFmtPrintF(outf, "%S", node->sub.DocumentType.name);
4214 
4215             if(node->sub.DocumentType.systemid)
4216             {
4217                 ajFmtPrintF(outf, " SYSTEM \"");
4218                 ajFmtPrintF(outf, "%S", node->sub.DocumentType.systemid);
4219                 ajFmtPrintF(outf, "\"");
4220             }
4221             else if(node->sub.DocumentType.publicid)
4222             {
4223                 ajFmtPrintF(outf, " PUBLIC \"");
4224                 ajFmtPrintF(outf, "%S", node->sub.DocumentType.publicid);
4225                 ajFmtPrintF(outf, "\"");
4226             }
4227 
4228             if(node->sub.DocumentType.internalsubset)
4229             {
4230                 ajFmtPrintF(outf, " [\n");
4231                 ajFmtPrintF(outf, "%S", node->sub.DocumentType.internalsubset);
4232                 ajFmtPrintF(outf, "]>\n");
4233             }
4234             else
4235                 ajFmtPrintF(outf, ">\n");
4236 
4237             iret = 1;
4238             break;
4239 
4240         case ajEDomNodeTypeDocumentFragment:
4241             break;
4242 
4243         default:
4244             ajDebug("ajDomWriteIndent got unexpected "
4245                     "AJAX DOM Node type %d.\n", node->type);
4246     }
4247 
4248     ajStrDel(&tmpstr);
4249 
4250     ajUser("return %d", iret);
4251 
4252     return iret;
4253 }
4254 
4255 
4256 
4257 
4258 /* @funcstatic domUserdataNew *************************************************
4259 **
4260 ** Create userdata object
4261 **
4262 ** @return [AjPDomUserdata] DOM Userdata for expat XML reading
4263 **
4264 ** @release 6.3.0
4265 ** @@
4266 ******************************************************************************/
4267 
domUserdataNew(void)4268 static AjPDomUserdata domUserdataNew(void)
4269 {
4270     AjPDomUserdata ret = NULL;
4271 
4272     AJNEW0(ret);
4273 
4274     ret->Buffer = ajStrNew();
4275     ret->Stack  = ajListNew();
4276     ret->Cdata  = ajFalse;
4277 
4278     return ret;
4279 }
4280 
4281 
4282 
4283 
4284 /* @funcstatic domUserdataDel *************************************************
4285 **
4286 ** Destroy userdata object
4287 **
4288 ** @param [d] thys [AjPDomUserdata*] Userdata object pointer
4289 **
4290 ** @return [void]
4291 **
4292 ** @release 6.3.0
4293 ** @@
4294 ******************************************************************************/
4295 
domUserdataDel(AjPDomUserdata * thys)4296 static void domUserdataDel(AjPDomUserdata *thys)
4297 {
4298     AjPDomUserdata pthis = NULL;
4299 
4300     if(!thys)
4301         return;
4302 
4303     pthis = *thys;
4304 
4305     if(!pthis)
4306         return;
4307 
4308     ajStrDel(&pthis->Buffer);
4309     ajListFree(&pthis->Stack);
4310 
4311     AJFREE(pthis);
4312 
4313     *thys = NULL;
4314 
4315     return;
4316 }
4317 
4318 
4319 
4320 
4321 /* @funcstatic domExpatStart **************************************************
4322 **
4323 ** XML reading Expat start function
4324 **
4325 ** @param [u] udata [void*] Userdata pointer
4326 ** @param [r] name [const XML_Char*] Name
4327 ** @param [r] atts [const XML_Char**] Attributes
4328 **
4329 ** @return [void]
4330 **
4331 ** @release 6.3.0
4332 ** @@
4333 ******************************************************************************/
4334 
domExpatStart(void * udata,const XML_Char * name,const XML_Char ** atts)4335 static void domExpatStart(void *udata, const XML_Char *name,
4336                           const XML_Char **atts)
4337 {
4338     AjPDomUserdata userdata = NULL;
4339     AjPDomDocument parent   = NULL;
4340     AjPDomDocument child    = NULL;
4341     ajint i = 0;
4342     AjPStr sname = NULL;
4343 
4344 
4345     if(!udata || !name || !atts)
4346         return;
4347 
4348     userdata = (AjPDomUserdata) udata;
4349 
4350     ajListPeek(userdata->Stack, (void **) &parent);
4351 
4352     if(!parent)
4353         return;
4354 
4355     ajStrAssignC(&userdata->Buffer, name);
4356 
4357     if(!(child = ajDomDocumentCreateElement(parent->ownerdocument,
4358                                             userdata->Buffer)))
4359         return;
4360 
4361     if(!ajDomNodeAppendChild(parent, child))
4362         return;
4363 
4364     sname = ajStrNew();
4365 
4366     for(i = 0; atts[i]; i += 2)
4367     {
4368         ajStrAssignC(&sname, atts[i]);
4369         ajStrAssignC(&userdata->Buffer, atts[i + 1]);
4370 
4371         ajDomElementSetAttribute(child, sname, userdata->Buffer);
4372     }
4373 
4374     ajListPush(userdata->Stack, (void *) child);
4375 
4376     ajStrDel(&sname);
4377 
4378     return;
4379 }
4380 
4381 
4382 
4383 
4384 /* @funcstatic domExpatEnd ****************************************************
4385 **
4386 ** XML reading Expat end function
4387 **
4388 ** @param [u] udata [void*] Userdata pointer
4389 ** @param [r] name [const XML_Char*] Name
4390 **
4391 ** @return [void]
4392 **
4393 ** @release 6.3.0
4394 ** @@
4395 ******************************************************************************/
4396 
domExpatEnd(void * udata,const XML_Char * name)4397 static void domExpatEnd(void *udata, const XML_Char *name)
4398 {
4399     AjPDomUserdata userdata = NULL;
4400     AjPDomDocument node = NULL;
4401 
4402     userdata = (AjPDomUserdata) udata;
4403 
4404     ajListPop(userdata->Stack, (void **)&node);
4405 
4406     (void) name;
4407 
4408     return;
4409 }
4410 
4411 
4412 
4413 
4414 /* @funcstatic domExpatChardata ***********************************************
4415 **
4416 ** XML reading Expat chardata function
4417 **
4418 ** @param [u] udata [void*] Userdata pointer
4419 ** @param [r] str [const XML_Char*] Char data
4420 ** @param [r] len [int] Length
4421 **
4422 ** @return [void]
4423 **
4424 ** @release 6.3.0
4425 ** @@
4426 ******************************************************************************/
4427 
domExpatChardata(void * udata,const XML_Char * str,int len)4428 static void domExpatChardata(void *udata, const XML_Char *str,
4429                              int len)
4430 {
4431     AjPDomUserdata userdata = NULL;
4432     AjPDomDocument parent   = NULL;
4433     AjPDomText txt = NULL;
4434 
4435     if(!udata || !str || !len)
4436         return;
4437 
4438     /*
4439     ** Care needs to be exercised with this function.
4440     ** First, it may need several callbacks to recover long strings.
4441     ** The operation might need to be converted to an append
4442     ** and only create nodes if the strings are complete
4443     **
4444     ** Its main use is to maintain the whitepace formatting  in
4445     ** the original XML file and could, in many cases, be
4446     ** ignored.
4447     */
4448 
4449     userdata = (AjPDomUserdata) udata;
4450 
4451     ajListPeek(userdata->Stack, (void **) &parent);
4452 
4453     if(!parent)
4454         return;
4455 
4456     ajStrAssignLenC(&userdata->Buffer, str, len);
4457 
4458     if(userdata->Cdata)
4459     {
4460         if(!(txt = ajDomDocumentCreateCDATASection(parent->ownerdocument,
4461                                                    userdata->Buffer)))
4462             return;
4463     }
4464     else
4465     {
4466         if(!(txt = ajDomDocumentCreateTextNode(parent->ownerdocument,
4467                                                userdata->Buffer)))
4468             return;
4469     }
4470 
4471 
4472     ajDomNodeAppendChild(parent, txt);
4473 
4474     return;
4475 }
4476 
4477 
4478 
4479 
4480 /* @funcstatic domExpatCdataStart *********************************************
4481 **
4482 ** XML reading Expat CDATA start function
4483 **
4484 ** @param [u] udata [void*] Userdata pointer
4485 **
4486 ** @return [void]
4487 **
4488 ** @release 6.3.0
4489 ** @@
4490 ******************************************************************************/
4491 
domExpatCdataStart(void * udata)4492 static void domExpatCdataStart(void *udata)
4493 {
4494     AjPDomUserdata userdata = NULL;
4495 
4496     if(!udata)
4497         return;
4498 
4499     userdata = (AjPDomUserdata) udata;
4500 
4501     userdata->Cdata = ajTrue;
4502 
4503     return;
4504 }
4505 
4506 
4507 
4508 
4509 /* @funcstatic domExpatCdataEnd ***********************************************
4510 **
4511 ** XML reading Expat CDATA end function
4512 **
4513 ** @param [u] udata [void*] Userdata pointer
4514 **
4515 ** @return [void]
4516 **
4517 ** @release 6.3.0
4518 ** @@
4519 ******************************************************************************/
4520 
domExpatCdataEnd(void * udata)4521 static void domExpatCdataEnd(void *udata)
4522 {
4523     AjPDomUserdata userdata = NULL;
4524 
4525     if(!udata)
4526         return;
4527 
4528     userdata = (AjPDomUserdata) udata;
4529 
4530     userdata->Cdata = ajFalse;
4531 
4532     return;
4533 }
4534 
4535 
4536 
4537 
4538 /* @funcstatic domExpatComment ************************************************
4539 **
4540 ** XML reading Expat comment function
4541 **
4542 ** @param [u] udata [void*] Userdata pointer
4543 ** @param [r] str [const XML_Char*] Char data
4544 **
4545 ** @return [void]
4546 **
4547 ** @release 6.3.0
4548 ** @@
4549 ******************************************************************************/
4550 
domExpatComment(void * udata,const XML_Char * str)4551 static void domExpatComment(void *udata, const XML_Char *str)
4552 {
4553     AjPDomUserdata userdata = NULL;
4554     AjPDomDocument parent   = NULL;
4555     AjPDomComment comment = NULL;
4556 
4557     if(!udata || !str)
4558         return;
4559 
4560     userdata = (AjPDomUserdata) udata;
4561 
4562     ajListPeek(userdata->Stack, (void **) &parent);
4563 
4564     if(!parent)
4565         return;
4566 
4567     ajStrAssignC(&userdata->Buffer, str);
4568 
4569     if((comment = ajDomDocumentCreateComment(parent->ownerdocument,
4570                                              userdata->Buffer)))
4571         ajDomNodeAppendChild(parent, comment);
4572 
4573     return;
4574 }
4575 
4576 
4577 
4578 
4579 /* @funcstatic domExpatProcessing *********************************************
4580 **
4581 ** XML reading Expat processing instruction function
4582 **
4583 ** @param [u] udata [void*] Userdata pointer
4584 ** @param [r] target [const XML_Char*] Char data
4585 ** @param [r] str [const XML_Char*] Char data
4586 **
4587 ** @return [void]
4588 **
4589 ** @release 6.3.0
4590 ** @@
4591 ******************************************************************************/
4592 
domExpatProcessing(void * udata,const XML_Char * target,const XML_Char * str)4593 static void domExpatProcessing(void *udata, const XML_Char *target,
4594                                const XML_Char *str)
4595 {
4596     AjPDomUserdata userdata = NULL;
4597     AjPDomDocument parent   = NULL;
4598     AjPDomPi pi = NULL;
4599     AjPStr t = NULL;
4600 
4601     if(!udata || !target || !!str)
4602         return;
4603 
4604     userdata = (AjPDomUserdata) udata;
4605 
4606     ajListPeek(userdata->Stack, (void **) &parent);
4607 
4608     if(!parent)
4609         return;
4610 
4611     t = ajStrNew();
4612 
4613     ajStrAssignC(&t, target);
4614     ajStrAssignC(&userdata->Buffer, str);
4615 
4616     if((pi = ajDomDocumentCreateProcessingInstruction(parent->ownerdocument,
4617                                                       t, userdata->Buffer)))
4618         ajDomNodeAppendChild(parent, pi);
4619 
4620     ajStrDel(&t);
4621 
4622     return;
4623 }
4624 
4625 
4626 
4627 
4628 /* @funcstatic domExpatXmlDecl ************************************************
4629 **
4630 ** XML reading Expat XML declaration function
4631 **
4632 ** @param [u] udata [void*] Userdata pointer
4633 ** @param [r] version [const XML_Char*] Version
4634 ** @param [r] encoding [const XML_Char*] Encoding
4635 ** @param [r] standalone [int] Standalone
4636 **
4637 ** @return [void]
4638 **
4639 ** @release 6.3.0
4640 ** @@
4641 ******************************************************************************/
4642 
domExpatXmlDecl(void * udata,const XML_Char * version,const XML_Char * encoding,int standalone)4643 static void domExpatXmlDecl(void *udata, const XML_Char *version,
4644                             const XML_Char *encoding, int standalone)
4645 {
4646     AjPDomUserdata userdata = NULL;
4647     AjPDomDocument document   = NULL;
4648 
4649     if(!udata)
4650         return;
4651 
4652     userdata = (AjPDomUserdata) udata;
4653 
4654     ajListPeek(userdata->Stack, (void **) &document);
4655 
4656     if(!document)
4657         return;
4658 
4659     if(!document->sub.Document.version)
4660         document->sub.Document.version = ajStrNew();
4661 
4662     if(!document->sub.Document.encoding)
4663         document->sub.Document.encoding = ajStrNew();
4664 
4665     if(version)
4666         ajStrAssignC(&document->sub.Document.version, version);
4667 
4668     if(encoding)
4669         ajStrAssignC(&document->sub.Document.encoding, encoding);
4670 
4671     document->sub.Document.standalone = standalone;
4672 
4673     return;
4674 }
4675 
4676 
4677 
4678 
4679 /* @funcstatic domExpatDoctypeStart *******************************************
4680 **
4681 ** XML reading Expat Doctype start function
4682 **
4683 ** @param [u] udata [void*] Userdata pointer
4684 ** @param [r] doctypename [const XML_Char*] Doctype name
4685 ** @param [r] sysid [const XML_Char*] Sysid
4686 ** @param [r] pubid [const XML_Char*] Sysid
4687 ** @param [r] hasinternalsubset [int] Internal subset flag
4688 **
4689 ** @return [void]
4690 **
4691 ** @release 6.3.0
4692 ** @@
4693 ******************************************************************************/
4694 
domExpatDoctypeStart(void * udata,const XML_Char * doctypename,const XML_Char * sysid,const XML_Char * pubid,int hasinternalsubset)4695 static void domExpatDoctypeStart(void *udata, const XML_Char *doctypename,
4696                                  const XML_Char *sysid, const XML_Char *pubid,
4697                                  int hasinternalsubset)
4698 {
4699     AjPDomUserdata userdata = NULL;
4700     AjPDomDocument doc      = NULL;
4701     AjPDomDocumentType type = NULL;
4702 
4703     (void) hasinternalsubset;
4704 
4705 
4706     if(!udata)
4707         return;
4708 
4709     userdata = (AjPDomUserdata) udata;
4710 
4711     if(!ajListPeek(userdata->Stack, (void **) &doc))
4712         return;
4713 
4714     if(!doc)
4715         return;
4716 
4717     if(doc->sub.Document.doctype)
4718     {
4719         ajDebug("domExpatDoctypeStart: doctype already exists");
4720         return;
4721     }
4722 
4723     ajStrAssignC(&userdata->Buffer, doctypename);
4724 
4725     if(!(type = ajDomImplementationCreateDocumentType(userdata->Buffer, NULL,
4726                                                       NULL)))
4727         return;
4728 
4729     if(sysid)
4730         ajStrAssignC(&type->sub.DocumentType.systemid, sysid);
4731 
4732     if(pubid)
4733         ajStrAssignC(&type->sub.DocumentType.publicid, pubid);
4734 
4735     if(!ajDomNodeAppendChild(doc, type))
4736         return;
4737 
4738     doc->sub.Document.doctype = type;
4739 
4740     ajListPush(userdata->Stack, (void *) type);
4741 
4742     return;
4743 }
4744 
4745 
4746 
4747 
4748 /* @funcstatic domExpatDoctypeEnd *********************************************
4749 **
4750 ** XML reading Expat Doctype end function
4751 **
4752 ** @param [u] udata [void*] Userdata pointer
4753 **
4754 ** @return [void]
4755 **
4756 ** @release 6.3.0
4757 ** @@
4758 ******************************************************************************/
4759 
domExpatDoctypeEnd(void * udata)4760 static void domExpatDoctypeEnd(void *udata)
4761 {
4762     AjPDomUserdata userdata = NULL;
4763     AjPDomDocumentType type = NULL;
4764 
4765     if(!udata)
4766         return;
4767 
4768     userdata = (AjPDomUserdata) udata;
4769 
4770     ajListPop(userdata->Stack, (void **)&type);
4771 
4772     return;
4773 }
4774 
4775 
4776 
4777 
4778 /* @funcstatic domExpatElement ************************************************
4779 **
4780 ** XML reading Expat Element function
4781 **
4782 ** @param [u] udata [void*] Userdata pointer
4783 ** @param [r] name [const XML_Char*] Name
4784 ** @param [d] model [XML_Content*] Model
4785 **
4786 ** @return [void]
4787 **
4788 ** @release 6.3.0
4789 ** @@
4790 ******************************************************************************/
4791 
domExpatElement(void * udata,const XML_Char * name,XML_Content * model)4792 static void domExpatElement(void *udata, const XML_Char *name,
4793                             XML_Content *model)
4794 {
4795     udata = NULL;
4796     name  = NULL;
4797 
4798     (void) udata;
4799     (void) name;
4800 
4801     free(model);
4802 
4803     return;
4804 }
4805 
4806 
4807 
4808 
4809 /* @funcstatic domExpatAttlist ************************************************
4810 **
4811 ** XML reading Expat attribute list function
4812 **
4813 ** @param [u] udata [void*] Userdata pointer
4814 ** @param [r] name [const XML_Char*] Element name
4815 ** @param [r] attname [const XML_Char*] Attribute Name
4816 ** @param [r] atttype [const XML_Char*] Attribute Type
4817 ** @param [r] deflt [const XML_Char*] Default
4818 ** @param [r] isrequired [int] Required flag
4819 **
4820 ** @return [void]
4821 **
4822 ** @release 6.3.0
4823 ** @@
4824 ******************************************************************************/
4825 
domExpatAttlist(void * udata,const XML_Char * name,const XML_Char * attname,const XML_Char * atttype,const XML_Char * deflt,int isrequired)4826 static void domExpatAttlist(void *udata, const XML_Char *name,
4827                             const XML_Char *attname, const XML_Char *atttype,
4828                             const XML_Char *deflt, int isrequired)
4829 {
4830     (void) udata;
4831     (void) name;
4832     (void) attname;
4833     (void) atttype;
4834     (void) deflt;
4835     (void) isrequired;
4836 
4837     return;
4838 }
4839 
4840 
4841 
4842 
4843 /* @funcstatic domExpatEntity *************************************************
4844 **
4845 ** XML reading Expat entity function
4846 **
4847 ** @param [u] udata [void*] Userdata pointer
4848 ** @param [r] entityname [const XML_Char*] Entity name
4849 ** @param [r] isparam [int] Flag for parameter entity
4850 ** @param [r] value [const XML_Char*] Value
4851 ** @param [r] lenval [int] Value length
4852 ** @param [r] base [const XML_Char*] Base
4853 ** @param [r] systemid [const XML_Char*] System ID
4854 ** @param [r] publicid [const XML_Char*] Public ID
4855 ** @param [r] notname [const XML_Char*] Notation name
4856 **
4857 ** @return [void]
4858 **
4859 ** @release 6.3.0
4860 ** @@
4861 ******************************************************************************/
4862 
domExpatEntity(void * udata,const XML_Char * entityname,int isparam,const XML_Char * value,int lenval,const XML_Char * base,const XML_Char * systemid,const XML_Char * publicid,const XML_Char * notname)4863 static void domExpatEntity(void *udata,
4864                            const XML_Char *entityname, int isparam,
4865                            const XML_Char *value, int lenval,
4866                            const XML_Char *base, const XML_Char *systemid,
4867                            const XML_Char *publicid, const XML_Char *notname)
4868 {
4869     AjPDomUserdata userdata = NULL;
4870     AjPDomDocumentType doctype = NULL;
4871     AjPDomEntity entity = NULL;
4872 
4873     (void) base;
4874 
4875     if(isparam)
4876         return;
4877 
4878     if(!udata)
4879         return;
4880 
4881     userdata = (AjPDomUserdata) udata;
4882 
4883     ajListPop(userdata->Stack, (void **)&doctype);
4884 
4885     if(!(entity = ajDomDocumentCreateNode(doctype->ownerdocument,
4886                                           ajEDomNodeTypeEntityNode)))
4887         return;
4888 
4889     ajStrAssignC(&entity->name, entityname);
4890 
4891     if(value)
4892         ajStrAssignLenC(&entity->value, value, lenval);
4893 
4894     if(publicid)
4895         ajStrAssignC(&entity->sub.Entity.publicid, publicid);
4896 
4897     if(systemid)
4898         ajStrAssignC(&entity->sub.Entity.systemid, systemid);
4899 
4900     if(notname)
4901         ajStrAssignC(&entity->sub.Entity.notationname, notname);
4902 
4903     ajDomNodeAppendChild(doctype, entity);
4904 
4905     return;
4906 }
4907 
4908 
4909 
4910 
4911 /* @funcstatic domExpatNotation ***********************************************
4912 **
4913 ** XML reading Expat notation function
4914 **
4915 ** @param [u] udata [void*] Userdata pointer
4916 ** @param [r] notname [const XML_Char*] Entity name
4917 ** @param [r] base [const XML_Char*] Base
4918 ** @param [r] systemid [const XML_Char*] System ID
4919 ** @param [r] publicid [const XML_Char*] Public ID
4920 **
4921 ** @return [void]
4922 **
4923 ** @release 6.3.0
4924 ** @@
4925 ******************************************************************************/
4926 
domExpatNotation(void * udata,const XML_Char * notname,const XML_Char * base,const XML_Char * systemid,const XML_Char * publicid)4927 static void domExpatNotation(void *udata, const XML_Char *notname,
4928                              const XML_Char *base, const XML_Char *systemid,
4929                              const XML_Char *publicid)
4930 {
4931     AjPDomUserdata userdata = NULL;
4932     AjPDomDocumentType doctype = NULL;
4933     AjPDomNotation notation = NULL;
4934 
4935     (void) base;
4936 
4937     if(!udata)
4938         return;
4939 
4940     userdata = (AjPDomUserdata) udata;
4941 
4942     ajListPop(userdata->Stack, (void **)&doctype);
4943 
4944     if(!(notation = ajDomDocumentCreateNode(doctype->ownerdocument,
4945                                             ajEDomNodeTypeNotation)))
4946         return;
4947 
4948     ajStrAssignC(&notation->name, notname);
4949 
4950     if(publicid)
4951         ajStrAssignC(&notation->sub.Notation.publicid, publicid);
4952 
4953     if(systemid)
4954         ajStrAssignC(&notation->sub.Notation.systemid, systemid);
4955 
4956     ajDomNodeAppendChild(doctype, notation);
4957 
4958     return;
4959 }
4960 
4961 
4962 
4963 
4964 /* @func ajDomReadFp **********************************************************
4965 **
4966 ** Read XML into memory from a file pointer
4967 **
4968 ** @param [u] node [AjPDomDocument] document to write
4969 ** @param [u] stream [FILE*] stream
4970 ** @return [ajint] zero OK, negative if error
4971 **
4972 ** @release 6.3.0
4973 ** @@
4974 ******************************************************************************/
4975 
ajDomReadFp(AjPDomDocument node,FILE * stream)4976 ajint ajDomReadFp(AjPDomDocument node, FILE *stream)
4977 {
4978     AjPDomUserdata userdata = NULL;
4979     XML_Parser parser = NULL;
4980     void *buf = NULL;
4981     ajlong n = 0L;
4982     ajint ret;
4983     ajint done;
4984 
4985     if(!node || !stream)
4986         return -1;
4987 
4988     parser = XML_ParserCreate(NULL);
4989     if(!parser)
4990         return -1;
4991 
4992 
4993 
4994     userdata = domUserdataNew();
4995 
4996     node->ownerdocument = node;
4997 
4998     ajListPush(userdata->Stack, (void *) node);
4999 
5000     XML_SetElementHandler(parser, domExpatStart, domExpatEnd);
5001     XML_SetCharacterDataHandler(parser, domExpatChardata);
5002     XML_SetCdataSectionHandler(parser, domExpatCdataStart, domExpatCdataEnd);
5003     XML_SetCommentHandler(parser, domExpatComment);
5004     XML_SetProcessingInstructionHandler(parser, domExpatProcessing);
5005     XML_SetXmlDeclHandler(parser, domExpatXmlDecl);
5006     XML_SetDoctypeDeclHandler(parser, domExpatDoctypeStart,
5007                               domExpatDoctypeEnd);
5008     XML_SetElementDeclHandler(parser, domExpatElement);
5009     XML_SetAttlistDeclHandler(parser, domExpatAttlist);
5010     XML_SetEntityDeclHandler(parser, domExpatEntity);
5011     XML_SetNotationDeclHandler(parser, domExpatNotation);
5012     XML_SetUserData(parser, userdata);
5013 
5014     ret = -1;
5015     for(;;)
5016     {
5017         if(!(buf = XML_GetBuffer(parser, AJXML_BUFSIZ)))
5018             break;
5019 
5020         if(!(n = fread(buf, 1, AJXML_BUFSIZ, stream)) && ferror(stream))
5021             break;
5022 
5023         if(!XML_ParseBuffer(parser, (int) n , (done = feof(stream))))
5024         {
5025             ajDebug("ajDomReadFp: Expat error [%s] at XML line %d",
5026                     XML_ErrorString(XML_GetErrorCode(parser)),
5027                     XML_GetCurrentLineNumber(parser));
5028             break;
5029         }
5030 
5031         if(done)
5032         {
5033             ret = 0;
5034             break;
5035         }
5036     }
5037 
5038     ajDomElementNormalise(node->sub.Document.documentelement);
5039 
5040     domUserdataDel(&userdata);
5041 
5042     XML_ParserFree(parser);
5043 
5044     return ret;
5045 }
5046 
5047 
5048 
5049 
5050 /* @func ajDomReadFilebuff ****************************************************
5051 **
5052 ** Read XML into memory from a file pointer
5053 **
5054 ** @param [u] node [AjPDomDocument] document to write
5055 ** @param [u] buff [AjPFilebuff] File buffer
5056 ** @return [ajint] zero OK, negative if error
5057 **
5058 ** @release 6.3.0
5059 ** @@
5060 ******************************************************************************/
5061 
ajDomReadFilebuff(AjPDomDocument node,AjPFilebuff buff)5062 ajint ajDomReadFilebuff(AjPDomDocument node, AjPFilebuff buff)
5063 {
5064     return ajDomReadFilebuffText(node, buff, 0, NULL);
5065 }
5066 
5067 
5068 
5069 
5070 /* @func ajDomReadFilebuffText ************************************************
5071 **
5072 ** Read XML into memory from a file pointer
5073 **
5074 ** @param [u] node [AjPDomDocument] document to write
5075 ** @param [u] buff [AjPFilebuff] File buffer
5076 ** @param [r] dotext [AjBool] If true, save input text to string
5077 ** @param [u] Pstr [AjPStr*] Text save buffer
5078 ** @return [ajint] zero OK, negative if error
5079 **
5080 ** @release 6.6.0
5081 ** @@
5082 ******************************************************************************/
5083 
ajDomReadFilebuffText(AjPDomDocument node,AjPFilebuff buff,AjBool dotext,AjPStr * Pstr)5084 ajint ajDomReadFilebuffText(AjPDomDocument node, AjPFilebuff buff,
5085                             AjBool dotext, AjPStr *Pstr)
5086 {
5087     AjPDomUserdata userdata = NULL;
5088     XML_Parser parser = NULL;
5089     int done = 0;
5090     ajint len = 0;
5091     AjPStr line = NULL;
5092 
5093     parser = XML_ParserCreate(NULL);
5094     if(!parser)
5095         return -1;
5096 
5097     userdata = domUserdataNew();
5098 
5099     node->ownerdocument = node;
5100 
5101     ajListPush(userdata->Stack, (void *) node);
5102 
5103     XML_SetElementHandler(parser, domExpatStart, domExpatEnd);
5104     XML_SetCharacterDataHandler(parser, domExpatChardata);
5105     XML_SetCdataSectionHandler(parser, domExpatCdataStart, domExpatCdataEnd);
5106     XML_SetCommentHandler(parser, domExpatComment);
5107     XML_SetProcessingInstructionHandler(parser, domExpatProcessing);
5108     XML_SetXmlDeclHandler(parser, domExpatXmlDecl);
5109     XML_SetDoctypeDeclHandler(parser, domExpatDoctypeStart,
5110                               domExpatDoctypeEnd);
5111     XML_SetElementDeclHandler(parser, domExpatElement);
5112     XML_SetAttlistDeclHandler(parser, domExpatAttlist);
5113     XML_SetEntityDeclHandler(parser, domExpatEntity);
5114     XML_SetNotationDeclHandler(parser, domExpatNotation);
5115     XML_SetUserData(parser, userdata);
5116 
5117     line = ajStrNew();
5118 
5119     do
5120     {
5121         ajBuffreadLineStore(buff, &line, dotext, Pstr);
5122         done = ajFilebuffIsEmpty(buff);
5123         len = ajStrGetLen(line);
5124 
5125         if(!XML_Parse(parser, MAJSTRGETPTR(line), len, done))
5126         {
5127             ajDebug("ajDomReadFilebuffText: %s at XML line %d\n",
5128                     XML_ErrorString(XML_GetErrorCode(parser)),
5129                     XML_GetCurrentLineNumber(parser));
5130 
5131             domUserdataDel(&userdata);
5132             XML_ParserFree(parser);
5133             ajStrDel(&line);
5134 
5135             return -1;
5136         }
5137 
5138     } while (!done);
5139 
5140     ajDomElementNormalise(node->sub.Document.documentelement);
5141 
5142     domUserdataDel(&userdata);
5143 
5144     XML_ParserFree(parser);
5145 
5146     ajStrDel(&line);
5147 
5148     return 0;
5149 }
5150 
5151 
5152 
5153 
5154 /* @func ajDomReadString ******************************************************
5155 **
5156 ** Read XML into memory from a string
5157 **
5158 ** @param [u] node [AjPDomDocument] document to write
5159 ** @param [u] str [AjPStr] XML string
5160 ** @return [ajint] zero OK, negative if error
5161 **
5162 ** @release 6.3.0
5163 ** @@
5164 ******************************************************************************/
5165 
ajDomReadString(AjPDomDocument node,AjPStr str)5166 ajint ajDomReadString(AjPDomDocument node, AjPStr str)
5167 {
5168     AjPDomUserdata userdata = NULL;
5169     XML_Parser parser = NULL;
5170     int done = 0;
5171     ajint len = 0;
5172 
5173     parser = XML_ParserCreate(NULL);
5174     if(!parser)
5175         return -1;
5176 
5177     userdata = domUserdataNew();
5178 
5179     node->ownerdocument = node;
5180 
5181     ajListPush(userdata->Stack, (void *) node);
5182 
5183     XML_SetElementHandler(parser, domExpatStart, domExpatEnd);
5184     XML_SetCharacterDataHandler(parser, domExpatChardata);
5185     XML_SetCdataSectionHandler(parser, domExpatCdataStart, domExpatCdataEnd);
5186     XML_SetCommentHandler(parser, domExpatComment);
5187     XML_SetProcessingInstructionHandler(parser, domExpatProcessing);
5188     XML_SetXmlDeclHandler(parser, domExpatXmlDecl);
5189     XML_SetDoctypeDeclHandler(parser, domExpatDoctypeStart,
5190                               domExpatDoctypeEnd);
5191     XML_SetElementDeclHandler(parser, domExpatElement);
5192     XML_SetAttlistDeclHandler(parser, domExpatAttlist);
5193     XML_SetEntityDeclHandler(parser, domExpatEntity);
5194     XML_SetNotationDeclHandler(parser, domExpatNotation);
5195     XML_SetUserData(parser, userdata);
5196 
5197     done = 1;
5198 
5199     len = ajStrGetLen(str);
5200 
5201     if(!XML_Parse(parser, MAJSTRGETPTR(str), len, done))
5202     {
5203         ajDebug("ajDomReadString: %s at XML line %d\n",
5204                 XML_ErrorString(XML_GetErrorCode(parser)),
5205                 XML_GetCurrentLineNumber(parser));
5206 
5207         return -1;
5208     }
5209 
5210     ajDomElementNormalise(node->sub.Document.documentelement);
5211 
5212     domUserdataDel(&userdata);
5213 
5214     XML_ParserFree(parser);
5215 
5216     return 0;
5217 }
5218 
5219 
5220 
5221 
5222 /* @func ajDomTextGetText *****************************************************
5223 **
5224 ** Return the text from a text node
5225 **
5226 ** @param [w] text [AjPDomText] text
5227 ** @return [AjPStr] text or NULL
5228 **
5229 ** @release 6.4.0
5230 ** @@
5231 ******************************************************************************/
5232 
ajDomTextGetText(AjPDomText text)5233 AjPStr ajDomTextGetText(AjPDomText text)
5234 {
5235     if(!text)
5236         return NULL;
5237 
5238     if(text->type != ajEDomNodeTypeText)
5239         return NULL;
5240 
5241     return text->value;
5242 }
5243 
5244 
5245 
5246 
5247 /* @func ajDomElementGetText **************************************************
5248 **
5249 ** Return the text from an element node
5250 **
5251 ** @param [w] element [AjPDomElement] element
5252 ** @return [AjPStr] text or NULL
5253 **
5254 ** @release 6.4.0
5255 ** @@
5256 ******************************************************************************/
5257 
ajDomElementGetText(AjPDomElement element)5258 AjPStr ajDomElementGetText(AjPDomElement element)
5259 {
5260     if(!element)
5261         return NULL;
5262 
5263     if(element->type != ajEDomNodeTypeElement)
5264         return NULL;
5265 
5266     if(!element->firstchild)
5267         return NULL;
5268 
5269     return ajDomTextGetText(element->firstchild);
5270 }
5271 
5272 
5273 
5274 
5275 /* @func ajDomElementGetNthChildByTagNameC ************************************
5276 **
5277 ** Returns the nth child of a named element
5278 **
5279 ** @param [u] doc [AjPDomDocument] doc
5280 ** @param [u] element [AjPDomElement] element
5281 ** @param [r] name [const char *] element name
5282 ** @param [r] n [ajint] number of child to get
5283 ** @return [AjPDomElement] child or NULL
5284 **
5285 ** @release 6.4.0
5286 ** @@
5287 ******************************************************************************/
5288 
ajDomElementGetNthChildByTagNameC(AjPDomDocument doc,AjPDomElement element,const char * name,ajint n)5289 AjPDomElement ajDomElementGetNthChildByTagNameC(AjPDomDocument doc,
5290                                                 AjPDomElement element,
5291                                                 const char *name,
5292                                                 ajint n)
5293 {
5294     AjPDomNodeList list;
5295     ajint len;
5296     AjPDomElement ret = NULL;
5297 
5298     list = ajDomElementGetElementsByTagNameC(element, name);
5299 
5300     if(!list)
5301         return NULL;
5302 
5303     len = ajDomNodeListGetLen(list);
5304 
5305     if(!len)
5306     {
5307         ajDomDocumentDestroyNodeList(doc, &list, AJDOMKEEP);
5308         return NULL;
5309     }
5310 
5311     if(n < 0 || n > len - 1)
5312     {
5313         ajDebug("ajDomElementgetNthChildByTagnameC: index out of range");
5314         ajDomDocumentDestroyNodeList(doc, &list, AJDOMKEEP);
5315         return NULL;
5316     }
5317 
5318     ret = ajDomNodeListItem(list, n);
5319 
5320     ajDomDocumentDestroyNodeList(doc, &list, AJDOMKEEP);
5321 
5322     return ret;
5323 }
5324 
5325 
5326 
5327 
5328 /* @func ajDomElementGetFirstChildByTagNameC **********************************
5329 **
5330 ** Returns the 1st child of a named element
5331 **
5332 ** @param [u] doc [AjPDomDocument] doc
5333 ** @param [u] element [AjPDomElement] element
5334 ** @param [r] name [const char*] element name
5335 ** @return [AjPDomElement] child or NULL
5336 **
5337 ** @release 6.4.0
5338 ** @@
5339 ******************************************************************************/
5340 
ajDomElementGetFirstChildByTagNameC(AjPDomDocument doc,AjPDomElement element,const char * name)5341 AjPDomElement ajDomElementGetFirstChildByTagNameC(AjPDomDocument doc,
5342                                                   AjPDomElement element,
5343                                                   const char *name)
5344 {
5345 
5346     return ajDomElementGetNthChildByTagNameC(doc, element, name, 0);
5347 }
5348