aboutsummaryrefslogtreecommitdiff
path: root/src/libjin-lua/scripts/utils/xml.lua
blob: 665e490f044cce0116c69196e5bd80ba2b8468d7 (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