aboutsummaryrefslogtreecommitdiff
path: root/src/libjin-lua/scripts/utils/xml.lua
blob: 8512dd3d018388b0d76f34916d2ba020e744dc69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

jin.utils = jin.utils or {}

------------------------------------------------------------------------------------------------------------------
-- https://github.com/Cluain/Lua-Simple-XML-Parser
------------------------------------------------------------------------------------------------------------------

local XmlParser = {}

function XmlParser.toXmlString(value)
  value = string.gsub(value, "&", "&"); -- '&' -> "&"
  value = string.gsub(value, "<", "&lt;"); -- '<' -> "&lt;"
  value = string.gsub(value, ">", "&gt;"); -- '>' -> "&gt;"
  value = string.gsub(value, "\"", "&quot;"); -- '"' -> "&quot;"
  value = string.gsub(value, "([^%w%&%;%p%\t% ])",
    function(c)
      return string.format("&#x%X;", string.byte(c))
    end);
  return value;
end

function XmlParser.fromXmlString(value)
  value = string.gsub(value, "&#x([%x]+)%;",
    function(h)
      return string.char(tonumber(h, 16))
    end);
  value = string.gsub(value, "&#([0-9]+)%;",
    function(h)
      return string.char(tonumber(h, 10))
    end);
  value = string.gsub(value, "&quot;", "\"");
  value = string.gsub(value, "&apos;", "'");
  value = string.gsub(value, "&gt;", ">");
  value = string.gsub(value, "&lt;", "<");
  value = string.gsub(value, "&amp;", "&");
  return value;
end

function XmlParser.parseArgs(node, s)
  string.gsub(s, "(%w+)=([\"'])(.-)%2", function(w, _, a)
    node:addProperty(w, XmlParser.fromXmlString(a))
  end)
end
  
local function newNode(name)
  local node = {}
  node.___value = nil
  node.___name = name
  node.___children = {}
  node.___props = {}

  function node:value() return self.___value end
  function node:setValue(val) self.___value = val end
  function node:name() return self.___name end
  function node:setName(name) self.___name = name end
  function node:children() return self.___children end
  function node:numChildren() return #self.___children end
  function node:addChild(child)
    if self[child:name()] ~= nil then
      if type(self[child:name()].name) == "function" then
        local tempTable = {}
        table.insert(tempTable, self[child:name()])
        self[child:name()] = tempTable
      end
      table.insert(self[child:name()], child)
    else
      self[child:name()] = child
    end
    table.insert(self.___children, child)
  end

  function node:properties() return self.___props end
  function node:numProperties() return #self.___props end
  function node:addProperty(name, value)
    local lName = "@" .. name
    if self[lName] ~= nil then
      if type(self[lName]) == "string" then
        local tempTable = {}
        table.insert(tempTable, self[lName])
        self[lName] = tempTable
      end
      table.insert(self[lName], value)
    else
      self[lName] = value
    end
    table.insert(self.___props, { name = name, value = self[name] })
  end

  return node
end

function XmlParser.parseXmlText(xmlText)
  local stack = {}
  local top = newNode()
  table.insert(stack, top)
  local ni, c, label, xarg, empty
  local i, j = 1, 1
  while true do
    ni, j, c, label, xarg, empty = string.find(xmlText, "<(%/?)([%w_:]+)(.-)(%/?)>", i)
    if not ni then break end
    local text = string.sub(xmlText, i, ni - 1);
    if not string.find(text, "^%s*$") then
      local lVal = (top:value() or "") .. XmlParser.fromXmlString(text)
      stack[#stack]:setValue(lVal)
    end
    if empty == "/" then -- empty element tag
      local lNode = newNode(label)
      XmlParser.parseArgs(lNode, xarg)
      top:addChild(lNode)
    elseif c == "" then -- start tag
      local lNode = newNode(label)
      XmlParser.parseArgs(lNode, xarg)
      table.insert(stack, lNode)
  top = lNode
    else -- end tag
      local toclose = table.remove(stack) -- remove top

      top = stack[#stack]
      if #stack < 1 then
        error("XmlParser. nothing to close with " .. label)
      end
      if toclose:name() ~= label then
        error("XmlParser. trying to close " .. toclose.name .. " with " .. label)
      end
      top:addChild(toclose)
    end
    i = j + 1
  end
  local text = string.sub(xmlText, i);
  if #stack > 1 then
    error("XmlParser. unclosed " .. stack[#stack]:name())
  end
  return top
end

function XmlParser.loadFile(xmlFilename, base)
  if not base then
    base = system.ResourceDirectory
  end

  local path = system.pathForFile(xmlFilename, base)
  local hFile, err = io.open(path, "r");

  if hFile and not err then
    local xmlText = hFile:read("*a"); -- read file content
    io.close(hFile);
    return XmlParser.parseXmlText(xmlText), nil;
  else
    print(err)
    return nil
  end
end

------------------------------------------------------------------------------------------------------------------
-- Export to Jin. 
------------------------------------------------------------------------------------------------------------------

jin.utils.xml = XmlParser