XXXDOM

From Pickwiki
Jump to navigationJump to search

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