function node.select(X, path, required)
local elements = path:split('/')
if not elements or #elements == 0 then
return
end
local thisNode = X
for i, v in ipairs(elements) do
local nodeType = xml.ELEMENT
local _, _, nodeName, predicate = v:find("^([^@]%w+)%[(.+)%]$")
if not nodeName then
nodeName = v
if nodeName:sub(1, 1) == '@' then
nodeType = xml.ATTRIBUTE
nodeName = nodeName:sub(2)
end
end
local nextNode
local count = thisNode:childCount(nodeName)
if count > 0 then
for j = 1, count do
local child = thisNode:child(nodeName, j)
if child:nodeType() == nodeType then
if predicate then
-- predicate = child index
local index = tonumber(predicate)
if index then
if index == j then
nextNode = child
break
end
end
-- predicate = child expression
local parts = predicate:split("=")
if parts then
local nodeValue = child:text(parts[1])
local compare = parts[2]:gsub("^['](%w+)[']$", "%1")
if nodeValue == compare then
nextNode = child
break
end
else
error('invalid predicate: ' .. predicate, 2)
end
else
nextNode = child
break
end
end
end
end
trace(nextNode)
-- set thisNode for next loop iteration
thisNode = nextNode
if not thisNode then
if required then
error('required path not found: ' .. v, 2)
end
return
end
end
return thisNode
end
function node.number(X, path, required, default)
if path then
X = X:select(path, required)
end
local nodeValue
if X then
if X:nodeType() == xml.ATTRIBUTE then
nodeValue = X:nodeValue()
else
for i = 1, #X do
if X[i]:nodeType() == xml.TEXT then
nodeValue = X[i]:nodeValue()
break
end
end
end
end
nodeValue = tonumber(nodeValue)
if nodeValue then
return nodeValue
elseif default then
return default
else
return 0
end
end
function node.text(X, path, required, default)
if path then
X = X:select(path, required)
end
local nodeValue
if X then
if X:nodeType() == xml.ATTRIBUTE then
nodeValue = X:nodeValue()
else
for i = 1, #X do
if X[i]:nodeType() == xml.TEXT then
nodeValue = X[i]:nodeValue()
break
end
end
end
end
if nodeValue then
return nodeValue
elseif default then
return default
else
return ''
end
end
function node.children(X, name, matchPath, matchValue)
local list = {}
for i = 1, #X do
if X[i]:nodeType() == xml.ELEMENT then
if X[i]:nodeName() == name then
if matchPath then
if X[i]:text(matchPath) == matchValue then
table.insert(list, X[i])
end
else
table.insert(list, X[i])
end
end
end
end
return list
end
function node.firstChild(X, name, matchPath, matchValue)
for i = 1, #X do
if X[i]:nodeType() == xml.ELEMENT then
if X[i]:nodeName() == name then
if matchPath then
if X[i]:text(matchPath) == matchValue then
return X[i]
end
else
return X[i]
end
end
end
end
end
function node.appendText(X, name, value)
local element = X:append(xml.ELEMENT, name)
element:append(xml.TEXT, value)
return element
end
function node.removeText(X)
for i = #X, 1, -1 do
if X[i]:nodeType() == xml.TEXT then
X[i] = nil
end
end
end
-- validate required parameters (List) are present in table (ignore arg1..argN parameters)
function checkParam(T, List)
if type(T) ~= 'table' then
error('invalid arguments, expected table', 3)
end
for k,v in pairs(List) do
for w,x in pairs(T) do
if w:find('arg') then
if w == 'arg' then error('Unknown parameter "'..w..'"', 3) end
else
if not List[w] then error('Unknown parameter "'..w..'"', 3) end
end
end
end
end
-- convert arg1..argN parameters into table array
function getArgs(P)
local args = {}
for k,v in pairs(P) do
if k:find('arg')==1 then
args[tonumber(k:sub(4))] = P[k]
end
end
return args
end
-- safely retry function
function retry(P)
checkParam(P, {func=0, retry=0, pause=0, funcname=0, errorfunc=0})
if type(P.func) ~= 'function' then
error('invalid func specified, expected pointer to function', 2)
end
local RetryCount = P.retry or -1
local Delay = P.pause or 10
local Fname = P.funcname
local Func = P.func
local ErrorFunc = P.errorfunc
local Success, ErrMsgOrReturnCode
local Args = getArgs(P)
local i = 0
if iguana.isTest() then RetryCount = 2 end
while true do
local R = {pcall(Func, unpack(Args))}
Success = R[1]
ErrMsgOrReturnCode = R[2]
if ErrorFunc then
Success = ErrorFunc(unpack(R))
end
if Success then
if i > 0 then
status('green', 'Operation succeeded on retry after ' .. i .. ' attempts.')
else
status('green')
end
return unpack(R,2)
else
local msg = 'Error executing operation '
if Fname then
msg = msg .. Fname .. '() '
end
if i == 0 then
msg = msg .. 'on first attempt.'
else
msg = msg .. 'on retry attempt ' .. i .. '.'
end
if RetryCount == -1 or i < RetryCount then
msg = msg .. '\nSleeping for ' .. Delay .. ' seconds before next retry.'
msg = msg .. '\nError: ' .. ErrMsgOrReturnCode
status('yellow', msg)
util.sleep(Delay * 1000)
i = i + 1
else
msg = msg .. '\nMax retries reached, stopping retries.'
status('yellow', msg)
return false, msg
end
end
end
end
function iguana.getChannelStatus(channelName)
if not channelName then
channelName = iguana.channelName()
end
local iguanaStatus = iguana.status()
local root = xml.parse(iguanaStatus)
for i = 1, root.IguanaStatus:childCount('Channel') do
local channel = root.IguanaStatus:child('Channel', i)
if channel.Name:nodeValue() == channelName then
return channel
end
end
end
function iguana.throttleChannel(queueSize, channelName)
local channelStatus = iguana.getChannelStatus(channelName)
trace(channelStatus)
if not channelStatus then
return true, 'unable to determine destination channel status'
end
local destChannelStatus = channelStatus.Status:nodeValue()
if not iguana.isTest() and destChannelStatus == 'off' then
return true, 'destination channel is currently off'
end
local destQueueSize = tonumber(channelStatus.MessagesQueued:nodeValue())
if not iguana.isTest() and destQueueSize > queueSize then
return true, 'destination queue size exceeds ' .. queueSize .. ' (currently ' .. destQueueSize .. ')'
end
-- do not throttle channel
return false
end
function status(color, text)
if text then
text = text .. '\n' .. os.date()
end
iguana.setChannelStatus{color = color, text = text}
end
function warning(text)
iguana.logWarning(text)
text = text .. '\n' .. os.date()
iguana.setChannelStatus{color = 'yellow', text = text}
end
How to use XMLUtil.lua
Import the above XMLUtils.lua into your translator, parse an xml, and call the following extended xml functions below:
Node.select()
USAGE
Select child node from the root XML node based on the XML path. Unlike traditional xml node drill down with nil validation, you can easily use select function to find the sub child of xml node.
REQUIRED PARAMETERS
path: xml child node path
required: If 'required' is true, the path must be exist otherwise throw “required path not found“ error.
RETURNS
The child node tree
Sample Code
local patientXML = recordXML:select("chargeTransaction/patient", true)
Node.number()
USAGE
Select child node from the root XML node based on the XML path.
REQUIRED PARAMETERS
XMLNode: the root of XML node
path: xml child node path
required: If 'required' is false and 'thisNode' is nil, the function returns nothing and exits.
RETURNS
The child node tree
SAMPLE CODE
local patientXML = chargeTransaction:number("batchId") .. '-' .. chargeTransaction:number("syncId")
Node.text()
USAGE
Keep the XML element but empty the XML element Text value
REQUIRED PARAMETERS
path: xml child node path
required: If 'required' is false and 'thisNode' is nil, the function returns nothing and exits.
RETURNS
The child node tree
SAMPLE CODE
Node.children()
USAGE
Select child node from the root XML node based on the XML path
REQUIRED PARAMETERS
XMLNode: the root of XML node
path: xml child node path
required: If 'required' is false and 'thisNode' is nil, the function returns nothing and exits.
RETURNS
The child node tree
SAMPLE CODE
Node.firstChild()
USAGE
Search for the first XML element that meets to criteria passed into the function.
REQUIRED PARAMETERS
Element Name: The name of the XML element.
Attribute Name: The attribute name that you want to be found. Make sure to include the “@” prefix.
Attribute Value: The attribute value attached to the attribute name to be found.
RETURNS
The first match for the XML element.
SAMPLE CODE
Node.removeText()
USAGE
Keep the XML element but empty the XML element Text value