XXXDOM
To bypass a memory leak with the XDOM functions in Universe 10.2.0 on AIX 5.3, I simulated some key functions as subroutines. They were about 30% slower than the native functions & do not attempt to recreate all the functionality, but they are infinitely more reliable, because they don't leak memory. YMMV.
For reference, here are some things that didn't help:
- XDOMClose() didn't close the XDOM objects.
- The issues fixed in the release notes for UV 10.2.7 & 10.2.12 were not specific enough for the company's appetite for an upgrade.
Contents:
- XXXDOMInclude - declare constants used by the other routines
- XXXDOMWrite - simulate XDOMWrite(), returns status code in first argument
- XXXDOMClose - simulate XDOMClose(), returns status code in first argument
- XXXDOMCreateNode - simulate XDOMCreateNode(), returns status code in first argument
- XXXDOMAddChild - simulate XDOMAddChild(), returns status code in first argument
- XXXDOMCreateRoot - simulate XDOMCreateRoot(), returns status code in first argument
To deploy: Save the 6 programs in the same source code file. Compile & catalog the subroutines.
To use: Call each subroutine with the same arguments as you would use each referenced function, except that the status code is returned in the first subroutine argument.
Please note the limitations in the comments of each program. This is not a replacement for the XDOM functions, and the node variables are not compatible with the internal structures produced by XDOM functions. It's just a workaround that worked for us, and will be easy to extend or replace.
! NAME
! XXXDOMInclude
!
! HISTORY
! 2008-Oct-20 dhochman Initial version
*
EQU XXXDOM.NODE.TYPE TO 1 ; * Type
EQU XXXDOM.NODE.NAME TO 2 ; * Node name
EQU XXXDOM.NODE.ATTR.KEY TO 3 ; * Attribute names for sorting (mv)
EQU XXXDOM.NODE.ATTR.NAME TO 4 ; * Attribute names (mv, assoc with 3)
EQU XXXDOM.NODE.ATTR.VALUE TO 5 ; * Attribute values (mv, assoc with 3)
EQU XXXDOM.NODE.CHILD.TYPE TO 6 ; * Child type (mv)
EQU XXXDOM.NODE.VALUE TO 7 ; * Child elements and text, starting point
EQU ASCII.AMP TO '&'
EQU ASCII.LT TO '<'
EQU ASCII.GT TO '>'
EQU ASCII.QUOT TO '"'
EQU ASCII.CR TO CHAR(13)
EQU ASCII.LF TO CHAR(10)
EQU ASCII.CRLF TO ASCII.CR:ASCII.LF
SUBROUTINE XXXDOMWrite(XDOMStatus, node, document, destination)
! NAME
! XXXDOMWrite
!
! TODO
! - Handle XML.TO.FILE
! - Handle more types
*
INCLUDE XXXDOMInclude
INCLUDE UNIVERSE.INCLUDE XML.H
*
XDOMStatus = XML.SUCCESS
document = ""
type = node<XXXDOM.NODE.TYPE>
*
BEGIN CASE
CASE destination EQ XML.TO.STRING
BEGIN CASE
CASE type EQ XDOM.ATTR.NODE
document = node<XXXDOM.NODE.NAME, 1>
document := '="':node<XXXDOM.NODE.VALUE, 1>:'"'
*
CASE type EQ XDOM.ELEMENT.NODE
IF (XXXDOM.NODE.NAME NE '') THEN
document := ASCII.LT:node<XXXDOM.NODE.NAME>
END
IF (node<XXXDOM.NODE.ATTR.NAME> NE "") THEN
attrCount = DCOUNT(node<XXXDOM.NODE.ATTR.NAME>, @VM)
FOR attrCtr = 1 TO attrCount
document := ' ':node<XXXDOM.NODE.ATTR.NAME, attrCtr>
document := '="':node<XXXDOM.NODE.ATTR.VALUE, attrCtr>:'"'
NEXT attrCtr
END
value = FIELD(node,@AM,XXXDOM.NODE.VALUE,99999)
IF (value EQ "") THEN
document := '/':ASCII.GT
END ELSE
value = CHANGE(value, @AM, ASCII.LF)
document := ASCII.GT
IF (value[1,1] EQ ASCII.LT) THEN
value = CHANGE(value, ' ':ASCII.LT, ' ':ASCII.LT)
value = CHANGE(value, ASCII.LF:ASCII.LT, ASCII.LF:' ':ASCII.LT)
value = ' ':value
document := ASCII.LF : value : ASCII.LF
END ELSE
document := value
END
IF (XXXDOM.NODE.NAME NE '') THEN
document := ASCII.LT:'/':node<XXXDOM.NODE.NAME>:ASCII.GT
END
END
*
CASE type EQ XDOM.TEXT.NODE
value = FIELD(node,@AM,XXXDOM.NODE.VALUE,99999)
document = value
*
CASE type EQ XDOM.DOC.NODE
document = FIELD(node,@AM,XXXDOM.NODE.VALUE,99999)
*
CASE 1
XDOMStatus = XML.ERROR
END CASE
*
CASE 1
XDOMStatus = XML.ERROR
*
CASE destination EQ XML.TO.FILE
NULL
END CASE
RETURN
SUBROUTINE XXXDOMClose(XDOMStatus, node)
! NAME
! XXXDOMClose
*
INCLUDE XXXDOMInclude
INCLUDE UNIVERSE.INCLUDE XML.H
*
XDOMStatus = XML.SUCCESS
node = ""
RETURN
SUBROUTINE [[XXXDOMCreateNode]](XDOMStatus, domHandle, label, value, type, node)
! NAME
! [[XXXDOMCreateNode]]
!
! DESCRIPTION
! Constructs an XDOM-like object.
!
! TODO
! - Handle domHandle
! - Handle more types
*
INCLUDE XXXDOMInclude
INCLUDE UNIVERSE.INCLUDE XML.H
*
XDOMStatus = XML.SUCCESS
node = ''
node<XXXDOM.NODE.TYPE> = type
*
BEGIN CASE
CASE type EQ XDOM.ATTR.NODE ; * label & value
node<XXXDOM.NODE.NAME> = label<1>
temp = value
IF INDEX(temp,ASCII.AMP,1) THEN temp = CHANGE(temp, ASCII.AMP, '&')
IF INDEX(temp,ASCII.QUOT,1) THEN temp = CHANGE(temp, ASCII.QUOT, '"')
IF INDEX(temp,ASCII.LT,1) THEN temp = CHANGE(temp, ASCII.LT, '<')
IF INDEX(temp,ASCII.GT,1) THEN temp = CHANGE(temp, ASCII.GT, '>')
node<XXXDOM.NODE.VALUE> = temp
CASE type EQ XDOM.ELEMENT.NODE ; * label only
node<XXXDOM.NODE.NAME> = label<1>
CASE type EQ XDOM.TEXT.NODE ; * value only
temp = value
IF INDEX(temp,ASCII.AMP,1) THEN temp = CHANGE(temp, ASCII.AMP, '&')
IF INDEX(temp,ASCII.QUOT,1) THEN temp = CHANGE(temp, ASCII.QUOT, '"')
IF INDEX(temp,ASCII.LT,1) THEN temp = CHANGE(temp, ASCII.LT, '<')
IF INDEX(temp,ASCII.GT,1) THEN temp = CHANGE(temp, ASCII.GT, '>')
node<XXXDOM.NODE.VALUE> = temp
CASE type EQ XDOM.DOC.NODE ; * document root
CASE 1
XDOMStatus = XML.ERROR
node = ''
END CASE
*
RETURN
SUBROUTINE [[XXXDOMAddChild]](XDOMStatus, parentNode, xpath, namespace, node, dupFlag)
! NAME
! [[XXXDOMAddChild]]
!
! TODO
! - Handle xpath
! - Handle namespace
! - Handle more types
! - Handle dupFlag
*
INCLUDE XXXDOMInclude
INCLUDE UNIVERSE.INCLUDE XML.H
*
XDOMStatus = XML.SUCCESS
type = node<XXXDOM.NODE.TYPE>
*
BEGIN CASE
CASE type EQ XDOM.ATTR.NODE
attrName = node<XXXDOM.NODE.NAME, 1>
attrValue = node<XXXDOM.NODE.VALUE, 1>
attrKey = LEN(attrName):" ":attrName
LOCATE(attrKey, parentNode, XXXDOM.NODE.ATTR.KEY; attrCtr; "AR") THEN
parentNode<XXXDOM.NODE.ATTR.VALUE, attrCtr> = attrValue
END ELSE
parentNode = INSERT(parentNode, XXXDOM.NODE.ATTR.KEY, attrCtr, 0, attrKey)
parentNode = INSERT(parentNode, XXXDOM.NODE.ATTR.NAME, attrCtr, 0, attrName)
parentNode = INSERT(parentNode, XXXDOM.NODE.ATTR.VALUE, attrCtr, 0, attrValue)
END
CASE type EQ XDOM.ELEMENT.NODE
CALL XXXDOMWrite(XDOMStatus, node, childString, XML.TO.STRING)
IF NOT(XDOMStatus) THEN
parentNode<XXXDOM.NODE.CHILD.TYPE, -1> = type
parentNode<XXXDOM.NODE.VALUE> = parentNode<XXXDOM.NODE.VALUE>
parentNode<-1> = childString
END
CASE type EQ XDOM.TEXT.NODE
CALL XXXDOMWrite(XDOMStatus, node, childString, XML.TO.STRING)
IF NOT(XDOMStatus) THEN
parentNode<XXXDOM.NODE.CHILD.TYPE, -1> = type
parentNode<XXXDOM.NODE.VALUE> = parentNode<XXXDOM.NODE.VALUE>
parentNode<-1> = childString
END
CASE 1
XDOMStatus = XML.ERROR
END CASE
*
RETURN
SUBROUTINE [[XXXDOMCreateRoot]](XDOMStatus, node)
! NAME
! [[XXXDOMCreateRoot]] - Constructs an XDOM-like document root node.
*
INCLUDE XXXDOMInclude
INCLUDE UNIVERSE.INCLUDE XML.H
*
CALL [[XXXDOMCreateNode]](XDOMStatus, "", "", "", XDOM.DOC.NODE, node)
*
RETURN