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