Code archives/File Utilities/XML code

This code has been declared by its author to be Public Domain code.

Download source code

XML code by GrahamK
(Posted 25 years ago)
Will add further comments/descriptions later
; XML load / parse / save functions

Type sdXMLnodelist
	Field node.sdxmlnode
	Field nextnode.sdxmlnodelist
	Field prevnode.sdxmlnodelist
End Type

; for internal use, do not use in code outside of this file
Type sdXMLworklist
	Field node.sdxmlnode
End Type


Type sdXMLnode
	Field tag$,value$,path$
	Field firstattr.sdXMLattr
	Field lastattr.sdXMLattr	
	Field attrcount,fileid
	Field endtag$
	
	; linkage functionality
	Field firstchild.sdXMLnode
	Field lastchild.sdXMLnode
	Field childcount
	Field nextnode.sdXMLnode
	Field prevnode.sdXMLnode
	Field parent.sdXMLnode
End Type

Type sdXMLattr
	Field name$,value$
	Field sibattr.sdXMLattr
	Field parent.sdxmlnode
End Type

Global SDXMLFILEID

Function sdReadXML.sdXMLnode(filename$)
	infile = ReadFile(filename$)
	SDXMLFILEID=MilliSecs()
	x.sdxmlnode = sdXMLReadNode(infile,Null)
	CloseFile infile
	Return x
End Function

Function sdWriteXML(filename$,node.sdxmlnode,writeroot=False)
	outfile = WriteFile(filename$)
	WriteLine outfile,&quot;<?xml version=&quot;+Chr$(34)+&quot;1.0&quot;+Chr$(34)+&quot; ?>&quot;
	sdXMLwriteNode(outfile,node)
	CloseFile outfile
End Function



Function sdXMLOpenNode.sdxmlnode(parent.sdxmlnode,tag$=&quot;&quot;)
	;gak debuglog &quot;Opening new node&quot;
	x.sdxmlnode = New sdxmlnode
	x\tag$=tag$
	x\fileid = SDXMLFILEID; global indicator to group type entries (allows multiple XML files to be used)
	sdXMLaddNode(parent,x)
	Return x
End Function

Function sdXMLCloseNode.sdxmlnode(node.sdxmlnode)
	;gak debuglog &quot;Closing node [&quot;+node\tag$+&quot;]&quot;
	If node\parent <> Null Then
		;gak debuglog &quot;Returning to parent [&quot;+node\parent\tag$+&quot;]&quot;
	Else
		;gak debuglog &quot;No Parent found&quot;
	End If
	Return node\parent
End Function

; adds node to end of list (need separate function for insert, or mod this on)
Function sdXMLAddNode(parent.sdxmlnode,node.sdxmlnode)
	If parent <> Null
		;gak debuglog &quot;Parent of node = [&quot;+parent\tag$+&quot;]&quot;
		If parent\childcount = 0 Then
			parent\firstchild = node
		Else
			parent\lastchild\nextnode = node
		End If
		node\prevnode = parent\lastchild
		parent\lastchild = node
		parent\childcount = parent\childcount +1
		node\path$ = parent\path$+parent\tag$
	End If
	node\parent = parent
	node\path$=node\path$+&quot;/&quot;
	;gak debuglog &quot;path to [&quot;+node\tag$+&quot;]={&quot;+node\path$+&quot;}&quot;
End Function


Function sdXMLDeleteNode(node.sdxmlnode)
	n.sdxmlnode = node\firstchild
	; delete any children recursively
	While n <> Null
		nn.sdxmlnode= n\nextnode
		sdXMLdeletenode(n)
		n = nn
	Wend

	; delete attributes for this node
	a.sdxmlattr = node\firstattr
	While a <> Null
		na.sdxmlattr = a\sibattr
		Delete a
		a = na
	Wend

	; dec parents child count
	If node\parent <> Null
		node\parent\childcount = node\parent\childcount -1
		
		; heal linkages
		If node\prevnode <> Null Then node\prevnode\nextnode = node\nextnode
		If node\nextnode <> Null Then node\nextnode\prevnode = node\prevnode
		If node\parent\firstchild = node Then node\parent\firstchild = node\nextnode
		If node\parent\lastchild = node Then node\parent\lastchild = node\prevnode
	End If
	; delete this node		
;	;gak debuglog &quot;DELETING:&quot;+node\tag$
	Delete node

End Function


; node functions

Function sdXMLfindNode.sdXMLnode(node.sdxmlnode,path$)
	;gak debuglog &quot;------------- Perfoming Find (&quot;+path$+&quot;)------------&quot;

	ret.sdXMLnode = Null
	p=Instr(path$,&quot;/&quot;)
	If p > 0 Then 
		tag$=Left$(path$,p-1)
		;gak debuglog &quot;Looking for [&quot;+tag$+&quot;]&quot;
		a.sdxmlnode = node
		While ret=Null And a<>Null 
			;gak debuglog &quot;Checking...[&quot;+a\tag$+&quot;]&quot;
			If Lower(tag$)=Lower(a\tag$) Then
				If p=Len(path$) Then
						;gak debuglog &quot;Found...&quot;
						ret = a
				Else
					If a\firstchild <> Null Then
						ret = sdxmlfindnode(a\firstchild,Mid$(path$,p+1))
					End If
				End If
			End If
			a = a\nextnode
		Wend
	End If
	Return ret
End Function

Function sdXMLDeleteList(nl.sdxmlnodelist)
	While nl <> Null
		na.sdxmlnodelist = nl\nextnode
		Delete nl
		nl = na
	Wend
End Function


Function sdXMLSelectNodes.sdxmlnodelist(node.sdxmlnode,path$,recurse=True)
	root.sdxmlnodelist=Null
	sdxmlselectnodesi(node,path$,recurse)
	prev.sdxmlnodelist=Null
	c = 0
	For wl.sdxmlworklist = Each sdxmlworklist
		c = c + 1
		nl.sdxmlnodelist = New sdxmlnodelist
		nl\node = wl\node
		If prev = Null Then 
			root = nl
			prev = nl
		Else
			prev\nextnode = nl
			nl\prevnode = prev
		End If
		prev = nl
		Delete wl
	Next
	;gak debuglog &quot;XML: &quot;+c+&quot; nodes selected&quot;
	Return root
End Function

; internal selection function, do not use outside this file
Function sdXMLSelectNodesI(node.sdxmlnode,path$,recurse=True)
	wl.sdXMLworklist=Null
	;gak debuglog &quot;------------- Perfoming Select (&quot;+path$+&quot;)------------&quot;
	If node = Null Then
		 ;gak debuglog &quot;Search node is null!!!&quot;
	End If
	ret.sdXMLnode = Null
	p=Instr(path$,&quot;/&quot;)
	If p > 0 Then 
		tag$=Left$(path$,p-1)
		a.sdxmlnode = node
		While a<>Null 
			;gak debuglog &quot;Looking for {&quot;+path$+&quot;} in {&quot;+a\path$+a\tag$+&quot;/}  {&quot;+Lower(Right$(a\path$+a\tag$+&quot;/&quot;,Len(path$)))+&quot;} @&quot;
			If Lower(path$)=Lower(Right$(a\path$+a\tag$+&quot;/&quot;,Len(path$))) Then
					wl = New sdXMLworklist
					wl\node = a
					;gak debuglog &quot;>>FOUND&quot;
			End If
			If a\firstchild <> Null And (recurse) Then
				sdXMLSelectNodesI(a\firstchild,path$)
			End If
			a = a\nextnode
		Wend
	End If

End Function

Function sdXMLNextNode.sdXMLnode(node.sdXMLnode)
	Return node\nextnode
End Function

Function sdXMLPrevNode.sdXMLnode(node.sdXMLnode)
	Return node\prevnode
End Function

Function sdXMLAddAttr(node.sdxmlnode,name$,value$)
	;gak debuglog &quot;XML:adding attribute &quot;+name$+&quot;=&quot;+value$+&quot; (&quot;+Len(value$)+&quot;)&quot;
	a.sdxmlattr = New sdxmlattr
	a\name$ = name$
	a\value$ = value$
	If node\attrcount = 0 Then
		node\firstattr = a
	Else
		node\lastattr\sibattr = a
	End If
	node\lastattr=a
	node\attrcount = node\attrcount + 1
	a\parent = node
End Function


Function sdXMLReadNode.sdxmlnode(infile,parent.sdXMLnode,pushed=False)
	mode = 0
	root.sdxmlnode = Null
	cnode.sdxmlnode = Null
	x.sdXMLnode = Null
	ispushed = False
	done = False
	While (Not done) And (Not Eof(infile))
		c = ReadByte(infile)
		If c<32 Then c=32
		ch$=Chr$(c)
;		;gak debuglog &quot;{&quot;+ch$+&quot;} &quot;+c+&quot; mode=&quot;+mode
		Select mode
		  Case 0 ; looking for the start of a tag, ignore everything else
			If ch$ = &quot;<&quot; Then 
				mode = 1; start collecting the tag
			End If
		  Case 1 ; check first byte of tag, ? special tag
		    If ch$ = &quot;?&quot; Or ch$ = &quot;!&quot; Then
		 		mode = 0; class special nodes as garbage & consume
			Else
				If ch$ = &quot;/&quot; Then 
					mode = 2 ; move to collecting end tag
					x\endtag$=ch$
					;gak debuglog &quot;** found end tag&quot;
				Else
					cnode=x
					x.sdXMLnode = sdXMLOpennode(cnode)
					If cnode=Null Then root=x
					x\tag$=ch$
					mode = 3 ; move to collecting start tag
				End If
			End If
		  Case 2 ; collect the tag name (close tag)
			If ch$=&quot;>&quot; Then 
				mode = 0 ; end of the close tag so jump out of loop
				;done = True
				x = sdXMLclosenode(x)
			Else 
				x\endtag$ = x\endtag$ + ch$
			End If
		  Case 3 ; collect the tag name 
			If ch$=&quot; &quot; Then 
				;gak debuglog &quot;TAG:&quot;+x\tag$
				mode = 4 ; tag name collected, move to collecting attributes
			Else 
				If ch$=&quot;/&quot; Then 
					;gak debuglog &quot;TAG:&quot;+x\tag$
					x\endtag$=x\tag$
					mode = 2; start/end tag combined, move to close
				Else
					If ch$=&quot;>&quot; Then
						;gak debuglog &quot;TAG:&quot;+x\tag$
						mode = 20; tag closed, move to collecting value
					Else
						x\tag$ = x\tag$ + ch$
					End If
				End If
			End If
		  Case 4 ; start to collect attributes
		    If Lower(ch$)>=&quot;a&quot; And Lower(ch$)<=&quot;z&quot; Then 
				aname$=ch$;
			    mode = 5; move to collect attribute name
			Else
				If ch$=&quot;>&quot; Then
					x\value$=&quot;&quot;
					mode = 20; tag closed, move to collecting value
				Else
					If ch$=&quot;/&quot; Then 
						mode = 2 ; move to collecting end tag
						x\endtag$=ch$
						;gak debuglog &quot;** found end tag&quot;
					End If
				End If
			End If
		  Case 5 ; collect attribute name
		    If ch$=&quot;=&quot; Then
			  ;gak debuglog &quot;ATT:&quot;+aname$
			  aval$=&quot;&quot;
			  mode = 6; move to collect attribute value
			Else
			  aname$=aname$+ch$
			End If
		  Case 6 ; collect attribute value
		    If c=34 Then
				mode = 7; move to collect string value
			Else
				If c <= 32 Then 
					;gak debuglog &quot;ATV:&quot;+aname$+&quot;=&quot;+aval$
					sdXMLAddAttr(x,aname$,aval$)
					mode = 4; start collecting a new attribute
				Else
			   		aval$=aval$+ch$
				End If
			End If
		  Case 7 ; collect string value
			If c=34 Then
				;gak debuglog &quot;ATV:&quot;+aname$+&quot;=&quot;+aval$
				sdxmlADDattr(x,aname$,aval$)
				mode = 4; go and collect next attribute
			Else
				aval$=aval$+ch$
			End If
		  Case 20 ; COLLECT THE VALUE PORTION
			If ch$=&quot;<&quot; Then 
				;gak debuglog &quot;VAL:&quot;+x\tag$+&quot;=&quot;+x\value$
				mode=1; go to tag checking
			Else
				x\value$=x\value$+ch$
			End If
		End Select
		
		If Eof(infile) Then done=True
	
	Wend

	Return root

End Function

; write out an XML node (and children)
Function sdXMLWriteNode(outfile,node.sdxmlnode,tab$=&quot;&quot;)
;	;gak debuglog &quot;Writing....&quot;+node\tag$+&quot;..&quot;
	s$=&quot;<&quot;+node\tag$
	a.sdxmlattr = node\firstattr
	While a<>Null
;		;gak debuglog &quot;Writing attr [&quot;+a\name$+&quot;]=[&quot;+a\value$+&quot;]&quot;
		s$ = s$+&quot; &quot;+Lower(a\name$)+&quot;=&quot;+Chr$(34)+a\value$+Chr$(34)
		a = a\sibattr
	Wend
	
	If node\value$=&quot;&quot; And node\childcount = 0 Then
		s$=s$+&quot;/>&quot;
		et$=&quot;&quot;
	Else
		s$=s$+&quot;>&quot;+node\value$
		et$=&quot;</&quot;+node\tag$+&quot;>&quot;
	End If
	
	WriteLine outfile,sdXMLcleanStr$(tab$+s$)
	n.sdxmlnode = node\firstchild
	While n <> Null
		sdXMLwriteNode(outfile,n,tab$+&quot;  &quot;)
		n = n\nextnode
	Wend
	
	If et$<> &quot;&quot; Then WriteLine outfile,sdXMLcleanStr$(tab$+et$)

End Function




; remove non-visible chars from the output stream
Function sdXMLCleanStr$(s$)
	a$=&quot;&quot;
	For i = 1 To Len(s$)
		If Asc(Mid$(s$,i,1))>=32 Then a$ = a$ +Mid$(s$,i,1)
	Next
	Return a$

End Function

; attribute functions
; return an attribute of a given name
Function sdXMLFindAttr.sdXMLattr(node.sdxmlnode,name$)
	ret.sdXMLattr = Null
	If node <> Null Then 
		a.sdxmlattr = node\firstattr
		done = False
		While ret=Null And a<>Null 
			If Lower(name$)=Lower(a\name$) Then
				ret = a
			End If
			a = a\sibattr
		Wend
	End If
	Return ret
End Function

; return an attribute value as a string
Function sdXMLAttrValueStr$(node.sdxmlnode,name$,dflt$=&quot;&quot;)
	ret$=dflt$
	a.sdxmlattr = sdXMLfindattr(node,name$)
	If a <> Null Then ret$=a\value$
	Return ret$
End Function

; return an attribute value as an integer
Function sdXMLAttrValueInt(node.sdxmlnode,name$,dflt=0)
	ret=dflt
	a.sdxmlattr = sdXMLfindattr(node,name$)
	If a <> Null Then ret=a\value
	Return ret
End Function

; return an attribute value as a float
Function sdXMLAttrValueFloat#(node.sdxmlnode,name$,dflt#=0)
	ret#=dflt#
	a.sdxmlattr = sdXMLfindattr(node,name$)
	If a <> Null Then ret#=a\value
	Return ret
End Function

;x.sdxmlnode = sdReadXML(&quot;test.xml&quot;)
;sdwritexml(&quot;test2.xml&quot;,x)

;f.sdxmlnode = sdxmlfindnode(x,&quot;BB3D/NODE/MESH/&quot;)
;If f <> Null Then
;	;gak debuglog &quot;FOUND!!!&quot;
;	sdxmldeletenode(f)	
;End If

;sdwritexml(&quot;test3.xml&quot;,x)

;nl.sdxmlnodelist = sdxmlselectnodes(x,&quot;/VERTEX/POS/&quot;)
;While nl <> Null;
;	;gak debuglog &quot;Found.....&quot;+nl\node\tag$
;	nl=nl\nextnode
;Wend
;sdxmldeleteList(nl);



;sdxmldeletenode(x)