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, "<");
3659 break;
3660 case '>':
3661 ajFmtPrintF(outf, ">");
3662 break;
3663 case '&':
3664 ajFmtPrintF(outf, "'");
3665 break;
3666 case '"':
3667 ajFmtPrintF(outf, """);
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, "<");
3736 break;
3737
3738 case '>':
3739 ajFmtPrintF(outf, ">");
3740 break;
3741
3742 case '&':
3743 ajFmtPrintF(outf, "'");
3744 break;
3745
3746 case '"':
3747 ajFmtPrintF(outf, """);
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(¬ation->name, notname);
4949
4950 if(publicid)
4951 ajStrAssignC(¬ation->sub.Notation.publicid, publicid);
4952
4953 if(systemid)
4954 ajStrAssignC(¬ation->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