flickerstreak@1
|
1 --[[
|
flickerstreak@1
|
2 Name: AceLibrary
|
flickerstreak@7
|
3 Revision: $Rev$
|
flickerstreak@1
|
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
|
flickerstreak@1
|
5 Inspired By: Iriel (iriel@vigilance-committee.org)
|
flickerstreak@1
|
6 Tekkub (tekkub@gmail.com)
|
flickerstreak@7
|
7 Revision: $Rev$
|
flickerstreak@1
|
8 Website: http://www.wowace.com/
|
flickerstreak@1
|
9 Documentation: http://www.wowace.com/index.php/AceLibrary
|
flickerstreak@1
|
10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
|
flickerstreak@1
|
11 Description: Versioning library to handle other library instances, upgrading,
|
flickerstreak@1
|
12 and proper access.
|
flickerstreak@1
|
13 It also provides a base for libraries to work off of, providing
|
flickerstreak@1
|
14 proper error tools. It is handy because all the errors occur in the
|
flickerstreak@1
|
15 file that called it, not in the library file itself.
|
flickerstreak@1
|
16 Dependencies: None
|
flickerstreak@7
|
17 License: LGPL v2.1
|
flickerstreak@1
|
18 ]]
|
flickerstreak@1
|
19
|
flickerstreak@1
|
20 local ACELIBRARY_MAJOR = "AceLibrary"
|
flickerstreak@7
|
21 local ACELIBRARY_MINOR = "$Revision: 20000 $"
|
flickerstreak@1
|
22
|
flickerstreak@1
|
23 local _G = getfenv(0)
|
flickerstreak@1
|
24 local previous = _G[ACELIBRARY_MAJOR]
|
flickerstreak@1
|
25 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
|
flickerstreak@1
|
26
|
flickerstreak@1
|
27 local function safecall(func,...)
|
flickerstreak@1
|
28 local success, err = pcall(func,...)
|
flickerstreak@7
|
29 if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end
|
flickerstreak@1
|
30 end
|
flickerstreak@1
|
31
|
flickerstreak@1
|
32 -- @table AceLibrary
|
flickerstreak@1
|
33 -- @brief System to handle all versioning of libraries.
|
flickerstreak@1
|
34 local AceLibrary = {}
|
flickerstreak@1
|
35 local AceLibrary_mt = {}
|
flickerstreak@1
|
36 setmetatable(AceLibrary, AceLibrary_mt)
|
flickerstreak@1
|
37
|
flickerstreak@1
|
38 local function error(self, message, ...)
|
flickerstreak@1
|
39 if type(self) ~= "table" then
|
flickerstreak@7
|
40 return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2)
|
flickerstreak@1
|
41 end
|
flickerstreak@1
|
42
|
flickerstreak@1
|
43 local stack = debugstack()
|
flickerstreak@1
|
44 if not message then
|
flickerstreak@7
|
45 local _,_,second = stack:find("\n(.-)\n")
|
flickerstreak@1
|
46 message = "error raised! " .. second
|
flickerstreak@1
|
47 else
|
flickerstreak@7
|
48 local arg = { ... } -- not worried about table creation, as errors don't happen often
|
flickerstreak@1
|
49
|
flickerstreak@1
|
50 for i = 1, #arg do
|
flickerstreak@1
|
51 arg[i] = tostring(arg[i])
|
flickerstreak@1
|
52 end
|
flickerstreak@1
|
53 for i = 1, 10 do
|
flickerstreak@1
|
54 table.insert(arg, "nil")
|
flickerstreak@1
|
55 end
|
flickerstreak@7
|
56 message = message:format(unpack(arg))
|
flickerstreak@1
|
57 end
|
flickerstreak@1
|
58
|
flickerstreak@1
|
59 if getmetatable(self) and getmetatable(self).__tostring then
|
flickerstreak@7
|
60 message = ("%s: %s"):format(tostring(self), message)
|
flickerstreak@1
|
61 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
|
flickerstreak@7
|
62 message = ("%s: %s"):format(self:GetLibraryVersion(), message)
|
flickerstreak@1
|
63 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
|
flickerstreak@7
|
64 message = ("%s: %s"):format(self.class:GetLibraryVersion(), message)
|
flickerstreak@1
|
65 end
|
flickerstreak@1
|
66
|
flickerstreak@7
|
67 local first = stack:gsub("\n.*", "")
|
flickerstreak@7
|
68 local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1")
|
flickerstreak@7
|
69 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
flickerstreak@1
|
70
|
flickerstreak@1
|
71
|
flickerstreak@1
|
72 local i = 0
|
flickerstreak@7
|
73 for s in stack:gmatch("\n([^\n]*)") do
|
flickerstreak@1
|
74 i = i + 1
|
flickerstreak@7
|
75 if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
|
flickerstreak@7
|
76 file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1")
|
flickerstreak@7
|
77 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
|
flickerstreak@1
|
78 break
|
flickerstreak@1
|
79 end
|
flickerstreak@1
|
80 end
|
flickerstreak@1
|
81 local j = 0
|
flickerstreak@7
|
82 for s in stack:gmatch("\n([^\n]*)") do
|
flickerstreak@1
|
83 j = j + 1
|
flickerstreak@7
|
84 if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then
|
flickerstreak@1
|
85 return _G.error(message, j+1)
|
flickerstreak@1
|
86 end
|
flickerstreak@1
|
87 end
|
flickerstreak@1
|
88 return _G.error(message, 2)
|
flickerstreak@1
|
89 end
|
flickerstreak@1
|
90
|
flickerstreak@1
|
91 local function assert(self, condition, message, ...)
|
flickerstreak@1
|
92 if not condition then
|
flickerstreak@1
|
93 if not message then
|
flickerstreak@1
|
94 local stack = debugstack()
|
flickerstreak@7
|
95 local _,_,second = stack:find("\n(.-)\n")
|
flickerstreak@1
|
96 message = "assertion failed! " .. second
|
flickerstreak@1
|
97 end
|
flickerstreak@1
|
98 return error(self, message, ...)
|
flickerstreak@1
|
99 end
|
flickerstreak@1
|
100 return condition
|
flickerstreak@1
|
101 end
|
flickerstreak@1
|
102
|
flickerstreak@1
|
103 local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
|
flickerstreak@1
|
104 if type(num) ~= "number" then
|
flickerstreak@1
|
105 return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
|
flickerstreak@1
|
106 elseif type(kind) ~= "string" then
|
flickerstreak@1
|
107 return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
|
flickerstreak@1
|
108 end
|
flickerstreak@1
|
109 local errored = false
|
flickerstreak@1
|
110 arg = type(arg)
|
flickerstreak@1
|
111 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
|
flickerstreak@7
|
112 local stack = debugstack()
|
flickerstreak@7
|
113 local _,_,func = stack:find("`argCheck'.-([`<].-['>])")
|
flickerstreak@1
|
114 if not func then
|
flickerstreak@7
|
115 _,_,func = stack:find("([`<].-['>])")
|
flickerstreak@1
|
116 end
|
flickerstreak@1
|
117 if kind5 then
|
flickerstreak@1
|
118 return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
|
flickerstreak@1
|
119 elseif kind4 then
|
flickerstreak@1
|
120 return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
|
flickerstreak@1
|
121 elseif kind3 then
|
flickerstreak@1
|
122 return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
|
flickerstreak@1
|
123 elseif kind2 then
|
flickerstreak@1
|
124 return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
|
flickerstreak@1
|
125 else
|
flickerstreak@1
|
126 return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
|
flickerstreak@1
|
127 end
|
flickerstreak@1
|
128 end
|
flickerstreak@1
|
129 end
|
flickerstreak@1
|
130
|
flickerstreak@1
|
131 local pcall
|
flickerstreak@1
|
132 do
|
flickerstreak@1
|
133 local function check(self, ret, ...)
|
flickerstreak@1
|
134 if not ret then
|
flickerstreak@7
|
135 local s = ...
|
flickerstreak@7
|
136 return error(self, (s:gsub(".-%.lua:%d-: ", "")))
|
flickerstreak@1
|
137 else
|
flickerstreak@1
|
138 return ...
|
flickerstreak@1
|
139 end
|
flickerstreak@1
|
140 end
|
flickerstreak@1
|
141
|
flickerstreak@1
|
142 function pcall(self, func, ...)
|
flickerstreak@1
|
143 return check(self, _G.pcall(func, ...))
|
flickerstreak@1
|
144 end
|
flickerstreak@1
|
145 end
|
flickerstreak@1
|
146
|
flickerstreak@1
|
147 local recurse = {}
|
flickerstreak@1
|
148 local function addToPositions(t, major)
|
flickerstreak@1
|
149 if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
|
flickerstreak@1
|
150 rawset(t, recurse, true)
|
flickerstreak@1
|
151 AceLibrary.positions[t] = major
|
flickerstreak@1
|
152 for k,v in pairs(t) do
|
flickerstreak@1
|
153 if type(v) == "table" and not rawget(v, recurse) then
|
flickerstreak@1
|
154 addToPositions(v, major)
|
flickerstreak@1
|
155 end
|
flickerstreak@1
|
156 if type(k) == "table" and not rawget(k, recurse) then
|
flickerstreak@1
|
157 addToPositions(k, major)
|
flickerstreak@1
|
158 end
|
flickerstreak@1
|
159 end
|
flickerstreak@1
|
160 local mt = getmetatable(t)
|
flickerstreak@1
|
161 if mt and not rawget(mt, recurse) then
|
flickerstreak@1
|
162 addToPositions(mt, major)
|
flickerstreak@1
|
163 end
|
flickerstreak@1
|
164 rawset(t, recurse, nil)
|
flickerstreak@1
|
165 end
|
flickerstreak@1
|
166 end
|
flickerstreak@1
|
167
|
flickerstreak@1
|
168 local function svnRevisionToNumber(text)
|
flickerstreak@1
|
169 if type(text) == "string" then
|
flickerstreak@7
|
170 if text:find("^%$Revision: (%d+) %$$") then
|
flickerstreak@7
|
171 return tonumber((text:gsub("^%$Revision: (%d+) %$$", "%1")))
|
flickerstreak@7
|
172 elseif text:find("^%$Rev: (%d+) %$$") then
|
flickerstreak@7
|
173 return tonumber((text:gsub("^%$Rev: (%d+) %$$", "%1")))
|
flickerstreak@7
|
174 elseif text:find("^%$LastChangedRevision: (%d+) %$$") then
|
flickerstreak@7
|
175 return tonumber((text:gsub("^%$LastChangedRevision: (%d+) %$$", "%1")))
|
flickerstreak@1
|
176 end
|
flickerstreak@1
|
177 elseif type(text) == "number" then
|
flickerstreak@1
|
178 return text
|
flickerstreak@1
|
179 end
|
flickerstreak@1
|
180 return nil
|
flickerstreak@1
|
181 end
|
flickerstreak@1
|
182
|
flickerstreak@1
|
183 local crawlReplace
|
flickerstreak@1
|
184 do
|
flickerstreak@1
|
185 local recurse = {}
|
flickerstreak@1
|
186 local function func(t, to, from)
|
flickerstreak@1
|
187 if recurse[t] then
|
flickerstreak@1
|
188 return
|
flickerstreak@1
|
189 end
|
flickerstreak@1
|
190 recurse[t] = true
|
flickerstreak@1
|
191 local mt = getmetatable(t)
|
flickerstreak@1
|
192 setmetatable(t, nil)
|
flickerstreak@1
|
193 rawset(t, to, rawget(t, from))
|
flickerstreak@1
|
194 rawset(t, from, nil)
|
flickerstreak@1
|
195 for k,v in pairs(t) do
|
flickerstreak@1
|
196 if v == from then
|
flickerstreak@1
|
197 t[k] = to
|
flickerstreak@1
|
198 elseif type(v) == "table" then
|
flickerstreak@1
|
199 if not recurse[v] then
|
flickerstreak@1
|
200 func(v, to, from)
|
flickerstreak@1
|
201 end
|
flickerstreak@1
|
202 end
|
flickerstreak@1
|
203
|
flickerstreak@1
|
204 if type(k) == "table" then
|
flickerstreak@1
|
205 if not recurse[k] then
|
flickerstreak@1
|
206 func(k, to, from)
|
flickerstreak@1
|
207 end
|
flickerstreak@1
|
208 end
|
flickerstreak@1
|
209 end
|
flickerstreak@1
|
210 setmetatable(t, mt)
|
flickerstreak@1
|
211 if mt then
|
flickerstreak@1
|
212 if mt == from then
|
flickerstreak@1
|
213 setmetatable(t, to)
|
flickerstreak@1
|
214 elseif not recurse[mt] then
|
flickerstreak@1
|
215 func(mt, to, from)
|
flickerstreak@1
|
216 end
|
flickerstreak@1
|
217 end
|
flickerstreak@1
|
218 end
|
flickerstreak@1
|
219 function crawlReplace(t, to, from)
|
flickerstreak@1
|
220 func(t, to, from)
|
flickerstreak@1
|
221 for k in pairs(recurse) do
|
flickerstreak@1
|
222 recurse[k] = nil
|
flickerstreak@1
|
223 end
|
flickerstreak@1
|
224 end
|
flickerstreak@1
|
225 end
|
flickerstreak@1
|
226
|
flickerstreak@1
|
227 -- @function destroyTable
|
flickerstreak@1
|
228 -- @brief remove all the contents of a table
|
flickerstreak@1
|
229 -- @param t table to destroy
|
flickerstreak@1
|
230 local function destroyTable(t)
|
flickerstreak@1
|
231 setmetatable(t, nil)
|
flickerstreak@1
|
232 for k,v in pairs(t) do
|
flickerstreak@1
|
233 t[k] = nil
|
flickerstreak@1
|
234 end
|
flickerstreak@1
|
235 end
|
flickerstreak@1
|
236
|
flickerstreak@1
|
237 local function isFrame(frame)
|
flickerstreak@1
|
238 return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
|
flickerstreak@1
|
239 end
|
flickerstreak@1
|
240
|
flickerstreak@1
|
241 -- @function copyTable
|
flickerstreak@1
|
242 -- @brief Create a shallow copy of a table and return it.
|
flickerstreak@1
|
243 -- @param from The table to copy from
|
flickerstreak@1
|
244 -- @return A shallow copy of the table
|
flickerstreak@1
|
245 local function copyTable(from)
|
flickerstreak@1
|
246 local to = {}
|
flickerstreak@1
|
247 for k,v in pairs(from) do
|
flickerstreak@1
|
248 to[k] = v
|
flickerstreak@1
|
249 end
|
flickerstreak@1
|
250 setmetatable(to, getmetatable(from))
|
flickerstreak@1
|
251 return to
|
flickerstreak@1
|
252 end
|
flickerstreak@1
|
253
|
flickerstreak@1
|
254 -- @function deepTransfer
|
flickerstreak@1
|
255 -- @brief Fully transfer all data, keeping proper previous table
|
flickerstreak@1
|
256 -- backreferences stable.
|
flickerstreak@1
|
257 -- @param to The table with which data is to be injected into
|
flickerstreak@1
|
258 -- @param from The table whose data will be injected into the first
|
flickerstreak@1
|
259 -- @param saveFields If available, a shallow copy of the basic data is saved
|
flickerstreak@1
|
260 -- in here.
|
flickerstreak@1
|
261 -- @param list The account of table references
|
flickerstreak@1
|
262 -- @param list2 The current status on which tables have been traversed.
|
flickerstreak@1
|
263 local deepTransfer
|
flickerstreak@1
|
264 do
|
flickerstreak@1
|
265 -- @function examine
|
flickerstreak@1
|
266 -- @brief Take account of all the table references to be shared
|
flickerstreak@1
|
267 -- between the to and from tables.
|
flickerstreak@1
|
268 -- @param to The table with which data is to be injected into
|
flickerstreak@1
|
269 -- @param from The table whose data will be injected into the first
|
flickerstreak@1
|
270 -- @param list An account of the table references
|
flickerstreak@1
|
271 local function examine(to, from, list, major)
|
flickerstreak@1
|
272 list[from] = to
|
flickerstreak@1
|
273 for k,v in pairs(from) do
|
flickerstreak@1
|
274 if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
|
flickerstreak@1
|
275 if from[k] == to[k] then
|
flickerstreak@1
|
276 list[from[k]] = to[k]
|
flickerstreak@1
|
277 elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
|
flickerstreak@1
|
278 list[from[k]] = from[k]
|
flickerstreak@1
|
279 elseif not list[from[k]] then
|
flickerstreak@1
|
280 examine(to[k], from[k], list, major)
|
flickerstreak@1
|
281 end
|
flickerstreak@1
|
282 end
|
flickerstreak@1
|
283 end
|
flickerstreak@1
|
284 return list
|
flickerstreak@1
|
285 end
|
flickerstreak@1
|
286
|
flickerstreak@1
|
287 function deepTransfer(to, from, saveFields, major, list, list2)
|
flickerstreak@1
|
288 setmetatable(to, nil)
|
flickerstreak@1
|
289 if not list then
|
flickerstreak@1
|
290 list = {}
|
flickerstreak@1
|
291 list2 = {}
|
flickerstreak@1
|
292 examine(to, from, list, major)
|
flickerstreak@1
|
293 end
|
flickerstreak@1
|
294 list2[to] = to
|
flickerstreak@1
|
295 for k,v in pairs(to) do
|
flickerstreak@1
|
296 if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
|
flickerstreak@1
|
297 if saveFields then
|
flickerstreak@1
|
298 saveFields[k] = v
|
flickerstreak@1
|
299 end
|
flickerstreak@1
|
300 to[k] = nil
|
flickerstreak@1
|
301 elseif v ~= _G then
|
flickerstreak@1
|
302 if saveFields then
|
flickerstreak@1
|
303 saveFields[k] = copyTable(v)
|
flickerstreak@1
|
304 end
|
flickerstreak@1
|
305 end
|
flickerstreak@1
|
306 end
|
flickerstreak@1
|
307 for k in pairs(from) do
|
flickerstreak@1
|
308 if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
|
flickerstreak@1
|
309 if not list2[to[k]] then
|
flickerstreak@1
|
310 deepTransfer(to[k], from[k], nil, major, list, list2)
|
flickerstreak@1
|
311 end
|
flickerstreak@1
|
312 to[k] = list[to[k]] or list2[to[k]]
|
flickerstreak@1
|
313 else
|
flickerstreak@1
|
314 rawset(to, k, from[k])
|
flickerstreak@1
|
315 end
|
flickerstreak@1
|
316 end
|
flickerstreak@1
|
317 setmetatable(to, getmetatable(from))
|
flickerstreak@1
|
318 local mt = getmetatable(to)
|
flickerstreak@1
|
319 if mt then
|
flickerstreak@1
|
320 if list[mt] then
|
flickerstreak@1
|
321 setmetatable(to, list[mt])
|
flickerstreak@1
|
322 elseif mt.__index and list[mt.__index] then
|
flickerstreak@1
|
323 mt.__index = list[mt.__index]
|
flickerstreak@1
|
324 end
|
flickerstreak@1
|
325 end
|
flickerstreak@1
|
326 destroyTable(from)
|
flickerstreak@1
|
327 end
|
flickerstreak@1
|
328 end
|
flickerstreak@1
|
329
|
flickerstreak@1
|
330 -- @method TryToLoadStandalone
|
flickerstreak@1
|
331 -- @brief Attempt to find and load a standalone version of the requested library
|
flickerstreak@1
|
332 -- @param major A string representing the major version
|
flickerstreak@1
|
333 -- @return If library is found, return values from the call to LoadAddOn are returned
|
flickerstreak@1
|
334 -- If the library has been requested previously, nil is returned.
|
flickerstreak@1
|
335 local function TryToLoadStandalone(major)
|
flickerstreak@1
|
336 if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end
|
flickerstreak@1
|
337 if AceLibrary.scannedlibs[major] then return end
|
flickerstreak@1
|
338
|
flickerstreak@1
|
339 AceLibrary.scannedlibs[major] = true
|
flickerstreak@1
|
340
|
flickerstreak@1
|
341 local name, _, _, enabled, loadable = GetAddOnInfo(major)
|
flickerstreak@1
|
342 if loadable then
|
flickerstreak@1
|
343 return LoadAddOn(name)
|
flickerstreak@1
|
344 end
|
flickerstreak@1
|
345
|
flickerstreak@1
|
346 for i=1,GetNumAddOns() do
|
flickerstreak@1
|
347 if GetAddOnMetadata(i, "X-AceLibrary-"..major) then
|
flickerstreak@1
|
348 local name, _, _, enabled, loadable = GetAddOnInfo(i)
|
flickerstreak@1
|
349 if loadable then
|
flickerstreak@1
|
350 return LoadAddOn(name)
|
flickerstreak@1
|
351 end
|
flickerstreak@1
|
352 end
|
flickerstreak@1
|
353 end
|
flickerstreak@1
|
354 end
|
flickerstreak@1
|
355
|
flickerstreak@1
|
356 -- @method IsNewVersion
|
flickerstreak@1
|
357 -- @brief Obtain whether the supplied version would be an upgrade to the
|
flickerstreak@1
|
358 -- current version. This allows for bypass code in library
|
flickerstreak@1
|
359 -- declaration.
|
flickerstreak@1
|
360 -- @param major A string representing the major version
|
flickerstreak@1
|
361 -- @param minor An integer or an svn revision string representing the minor version
|
flickerstreak@1
|
362 -- @return whether the supplied version would be newer than what is
|
flickerstreak@1
|
363 -- currently available.
|
flickerstreak@1
|
364 function AceLibrary:IsNewVersion(major, minor)
|
flickerstreak@1
|
365 argCheck(self, major, 2, "string")
|
flickerstreak@1
|
366 TryToLoadStandalone(major)
|
flickerstreak@1
|
367
|
flickerstreak@1
|
368 if type(minor) == "string" then
|
flickerstreak@1
|
369 local m = svnRevisionToNumber(minor)
|
flickerstreak@1
|
370 if m then
|
flickerstreak@1
|
371 minor = m
|
flickerstreak@1
|
372 else
|
flickerstreak@7
|
373 _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
|
flickerstreak@1
|
374 end
|
flickerstreak@1
|
375 end
|
flickerstreak@1
|
376 argCheck(self, minor, 3, "number")
|
flickerstreak@1
|
377 local data = self.libs[major]
|
flickerstreak@1
|
378 if not data then
|
flickerstreak@1
|
379 return true
|
flickerstreak@1
|
380 end
|
flickerstreak@1
|
381 return data.minor < minor
|
flickerstreak@1
|
382 end
|
flickerstreak@1
|
383
|
flickerstreak@1
|
384 -- @method HasInstance
|
flickerstreak@1
|
385 -- @brief Returns whether an instance exists. This allows for optional support of a library.
|
flickerstreak@1
|
386 -- @param major A string representing the major version.
|
flickerstreak@1
|
387 -- @param minor (optional) An integer or an svn revision string representing the minor version.
|
flickerstreak@1
|
388 -- @return Whether an instance exists.
|
flickerstreak@1
|
389 function AceLibrary:HasInstance(major, minor)
|
flickerstreak@1
|
390 argCheck(self, major, 2, "string")
|
flickerstreak@1
|
391 TryToLoadStandalone(major)
|
flickerstreak@1
|
392
|
flickerstreak@1
|
393 if minor then
|
flickerstreak@1
|
394 if type(minor) == "string" then
|
flickerstreak@1
|
395 local m = svnRevisionToNumber(minor)
|
flickerstreak@1
|
396 if m then
|
flickerstreak@1
|
397 minor = m
|
flickerstreak@1
|
398 else
|
flickerstreak@7
|
399 _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
|
flickerstreak@1
|
400 end
|
flickerstreak@1
|
401 end
|
flickerstreak@1
|
402 argCheck(self, minor, 3, "number")
|
flickerstreak@1
|
403 if not self.libs[major] then
|
flickerstreak@1
|
404 return
|
flickerstreak@1
|
405 end
|
flickerstreak@1
|
406 return self.libs[major].minor == minor
|
flickerstreak@1
|
407 end
|
flickerstreak@1
|
408 return self.libs[major] and true
|
flickerstreak@1
|
409 end
|
flickerstreak@1
|
410
|
flickerstreak@1
|
411 -- @method GetInstance
|
flickerstreak@1
|
412 -- @brief Returns the library with the given major/minor version.
|
flickerstreak@1
|
413 -- @param major A string representing the major version.
|
flickerstreak@1
|
414 -- @param minor (optional) An integer or an svn revision string representing the minor version.
|
flickerstreak@1
|
415 -- @return The library with the given major/minor version.
|
flickerstreak@1
|
416 function AceLibrary:GetInstance(major, minor)
|
flickerstreak@1
|
417 argCheck(self, major, 2, "string")
|
flickerstreak@1
|
418 TryToLoadStandalone(major)
|
flickerstreak@1
|
419
|
flickerstreak@1
|
420 local data = self.libs[major]
|
flickerstreak@1
|
421 if not data then
|
flickerstreak@7
|
422 _G.error(("Cannot find a library instance of %s."):format(major), 2)
|
flickerstreak@1
|
423 return
|
flickerstreak@1
|
424 end
|
flickerstreak@1
|
425 if minor then
|
flickerstreak@1
|
426 if type(minor) == "string" then
|
flickerstreak@1
|
427 local m = svnRevisionToNumber(minor)
|
flickerstreak@1
|
428 if m then
|
flickerstreak@1
|
429 minor = m
|
flickerstreak@1
|
430 else
|
flickerstreak@7
|
431 _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
|
flickerstreak@1
|
432 end
|
flickerstreak@1
|
433 end
|
flickerstreak@1
|
434 argCheck(self, minor, 2, "number")
|
flickerstreak@1
|
435 if data.minor ~= minor then
|
flickerstreak@7
|
436 _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2)
|
flickerstreak@1
|
437 end
|
flickerstreak@1
|
438 end
|
flickerstreak@1
|
439 return data.instance
|
flickerstreak@1
|
440 end
|
flickerstreak@1
|
441
|
flickerstreak@1
|
442 -- Syntax sugar. AceLibrary("FooBar-1.0")
|
flickerstreak@1
|
443 AceLibrary_mt.__call = AceLibrary.GetInstance
|
flickerstreak@1
|
444
|
flickerstreak@1
|
445 local donothing = function() end
|
flickerstreak@1
|
446
|
flickerstreak@1
|
447 local AceEvent
|
flickerstreak@1
|
448
|
flickerstreak@1
|
449 -- @method Register
|
flickerstreak@1
|
450 -- @brief Registers a new version of a given library.
|
flickerstreak@1
|
451 -- @param newInstance the library to register
|
flickerstreak@1
|
452 -- @param major the major version of the library
|
flickerstreak@1
|
453 -- @param minor the minor version of the library
|
flickerstreak@1
|
454 -- @param activateFunc (optional) A function to be called when the library is
|
flickerstreak@1
|
455 -- fully activated. Takes the arguments
|
flickerstreak@1
|
456 -- (newInstance [, oldInstance, oldDeactivateFunc]). If
|
flickerstreak@1
|
457 -- oldInstance is given, you should probably call
|
flickerstreak@1
|
458 -- oldDeactivateFunc(oldInstance).
|
flickerstreak@1
|
459 -- @param deactivateFunc (optional) A function to be called by a newer library's
|
flickerstreak@1
|
460 -- activateFunc.
|
flickerstreak@1
|
461 -- @param externalFunc (optional) A function to be called whenever a new
|
flickerstreak@1
|
462 -- library is registered.
|
flickerstreak@1
|
463 function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
|
flickerstreak@1
|
464 argCheck(self, newInstance, 2, "table")
|
flickerstreak@1
|
465 argCheck(self, major, 3, "string")
|
flickerstreak@1
|
466 if major ~= ACELIBRARY_MAJOR then
|
flickerstreak@1
|
467 for k,v in pairs(_G) do
|
flickerstreak@1
|
468 if v == newInstance then
|
flickerstreak@7
|
469 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
|
flickerstreak@1
|
470 end
|
flickerstreak@1
|
471 end
|
flickerstreak@1
|
472 end
|
flickerstreak@7
|
473 if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then
|
flickerstreak@1
|
474 _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2)
|
flickerstreak@1
|
475 end
|
flickerstreak@1
|
476 if type(minor) == "string" then
|
flickerstreak@1
|
477 local m = svnRevisionToNumber(minor)
|
flickerstreak@1
|
478 if m then
|
flickerstreak@1
|
479 minor = m
|
flickerstreak@1
|
480 else
|
flickerstreak@7
|
481 _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2)
|
flickerstreak@1
|
482 end
|
flickerstreak@1
|
483 end
|
flickerstreak@1
|
484 argCheck(self, minor, 4, "number")
|
flickerstreak@1
|
485 if math.floor(minor) ~= minor or minor < 0 then
|
flickerstreak@1
|
486 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
|
flickerstreak@1
|
487 end
|
flickerstreak@1
|
488 argCheck(self, activateFunc, 5, "function", "nil")
|
flickerstreak@1
|
489 argCheck(self, deactivateFunc, 6, "function", "nil")
|
flickerstreak@1
|
490 argCheck(self, externalFunc, 7, "function", "nil")
|
flickerstreak@1
|
491 if not deactivateFunc then
|
flickerstreak@1
|
492 deactivateFunc = donothing
|
flickerstreak@1
|
493 end
|
flickerstreak@1
|
494 local data = self.libs[major]
|
flickerstreak@1
|
495 if not data then
|
flickerstreak@1
|
496 -- This is new
|
flickerstreak@1
|
497 local instance = copyTable(newInstance)
|
flickerstreak@1
|
498 crawlReplace(instance, instance, newInstance)
|
flickerstreak@1
|
499 destroyTable(newInstance)
|
flickerstreak@1
|
500 if AceLibrary == newInstance then
|
flickerstreak@1
|
501 self = instance
|
flickerstreak@1
|
502 AceLibrary = instance
|
flickerstreak@1
|
503 end
|
flickerstreak@1
|
504 self.libs[major] = {
|
flickerstreak@1
|
505 instance = instance,
|
flickerstreak@1
|
506 minor = minor,
|
flickerstreak@1
|
507 deactivateFunc = deactivateFunc,
|
flickerstreak@1
|
508 externalFunc = externalFunc,
|
flickerstreak@1
|
509 }
|
flickerstreak@1
|
510 rawset(instance, 'GetLibraryVersion', function(self)
|
flickerstreak@1
|
511 return major, minor
|
flickerstreak@1
|
512 end)
|
flickerstreak@1
|
513 if not rawget(instance, 'error') then
|
flickerstreak@1
|
514 rawset(instance, 'error', error)
|
flickerstreak@1
|
515 end
|
flickerstreak@1
|
516 if not rawget(instance, 'assert') then
|
flickerstreak@1
|
517 rawset(instance, 'assert', assert)
|
flickerstreak@1
|
518 end
|
flickerstreak@1
|
519 if not rawget(instance, 'argCheck') then
|
flickerstreak@1
|
520 rawset(instance, 'argCheck', argCheck)
|
flickerstreak@1
|
521 end
|
flickerstreak@1
|
522 if not rawget(instance, 'pcall') then
|
flickerstreak@1
|
523 rawset(instance, 'pcall', pcall)
|
flickerstreak@1
|
524 end
|
flickerstreak@1
|
525 addToPositions(instance, major)
|
flickerstreak@1
|
526 if activateFunc then
|
flickerstreak@1
|
527 safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil
|
flickerstreak@1
|
528
|
flickerstreak@1
|
529 --[[ if major ~= ACELIBRARY_MAJOR then
|
flickerstreak@1
|
530 for k,v in pairs(_G) do
|
flickerstreak@1
|
531 if v == instance then
|
flickerstreak@7
|
532 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
|
flickerstreak@1
|
533 end
|
flickerstreak@1
|
534 end
|
flickerstreak@1
|
535 end]]
|
flickerstreak@1
|
536 end
|
flickerstreak@1
|
537
|
flickerstreak@1
|
538 if externalFunc then
|
flickerstreak@1
|
539 for k,data in pairs(self.libs) do
|
flickerstreak@1
|
540 if k ~= major then
|
flickerstreak@1
|
541 safecall(externalFunc, instance, k, data.instance)
|
flickerstreak@1
|
542 end
|
flickerstreak@1
|
543 end
|
flickerstreak@1
|
544 end
|
flickerstreak@1
|
545
|
flickerstreak@1
|
546 for k,data in pairs(self.libs) do
|
flickerstreak@1
|
547 if k ~= major and data.externalFunc then
|
flickerstreak@1
|
548 safecall(data.externalFunc, data.instance, major, instance)
|
flickerstreak@1
|
549 end
|
flickerstreak@1
|
550 end
|
flickerstreak@1
|
551 if major == "AceEvent-2.0" then
|
flickerstreak@1
|
552 AceEvent = instance
|
flickerstreak@1
|
553 end
|
flickerstreak@1
|
554 if AceEvent then
|
flickerstreak@1
|
555 AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
|
flickerstreak@1
|
556 end
|
flickerstreak@1
|
557
|
flickerstreak@1
|
558 return instance
|
flickerstreak@1
|
559 end
|
flickerstreak@1
|
560 local instance = data.instance
|
flickerstreak@1
|
561 if minor <= data.minor then
|
flickerstreak@1
|
562 -- This one is already obsolete, raise an error.
|
flickerstreak@7
|
563 _G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2)
|
flickerstreak@1
|
564 return
|
flickerstreak@1
|
565 end
|
flickerstreak@1
|
566 -- This is an update
|
flickerstreak@1
|
567 local oldInstance = {}
|
flickerstreak@1
|
568
|
flickerstreak@1
|
569 addToPositions(newInstance, major)
|
flickerstreak@1
|
570 local isAceLibrary = (AceLibrary == newInstance)
|
flickerstreak@1
|
571 local old_error, old_assert, old_argCheck, old_pcall
|
flickerstreak@1
|
572 if isAceLibrary then
|
flickerstreak@1
|
573 self = instance
|
flickerstreak@1
|
574 AceLibrary = instance
|
flickerstreak@1
|
575
|
flickerstreak@1
|
576 old_error = instance.error
|
flickerstreak@1
|
577 old_assert = instance.assert
|
flickerstreak@1
|
578 old_argCheck = instance.argCheck
|
flickerstreak@1
|
579 old_pcall = instance.pcall
|
flickerstreak@1
|
580
|
flickerstreak@1
|
581 self.error = error
|
flickerstreak@1
|
582 self.assert = assert
|
flickerstreak@1
|
583 self.argCheck = argCheck
|
flickerstreak@1
|
584 self.pcall = pcall
|
flickerstreak@1
|
585 end
|
flickerstreak@1
|
586 deepTransfer(instance, newInstance, oldInstance, major)
|
flickerstreak@1
|
587 crawlReplace(instance, instance, newInstance)
|
flickerstreak@1
|
588 local oldDeactivateFunc = data.deactivateFunc
|
flickerstreak@1
|
589 data.minor = minor
|
flickerstreak@1
|
590 data.deactivateFunc = deactivateFunc
|
flickerstreak@1
|
591 data.externalFunc = externalFunc
|
flickerstreak@1
|
592 rawset(instance, 'GetLibraryVersion', function(self)
|
flickerstreak@1
|
593 return major, minor
|
flickerstreak@1
|
594 end)
|
flickerstreak@1
|
595 if not rawget(instance, 'error') then
|
flickerstreak@1
|
596 rawset(instance, 'error', error)
|
flickerstreak@1
|
597 end
|
flickerstreak@1
|
598 if not rawget(instance, 'assert') then
|
flickerstreak@1
|
599 rawset(instance, 'assert', assert)
|
flickerstreak@1
|
600 end
|
flickerstreak@1
|
601 if not rawget(instance, 'argCheck') then
|
flickerstreak@1
|
602 rawset(instance, 'argCheck', argCheck)
|
flickerstreak@1
|
603 end
|
flickerstreak@1
|
604 if not rawget(instance, 'pcall') then
|
flickerstreak@1
|
605 rawset(instance, 'pcall', pcall)
|
flickerstreak@1
|
606 end
|
flickerstreak@1
|
607 if isAceLibrary then
|
flickerstreak@1
|
608 for _,v in pairs(self.libs) do
|
flickerstreak@1
|
609 local i = type(v) == "table" and v.instance
|
flickerstreak@1
|
610 if type(i) == "table" then
|
flickerstreak@1
|
611 if not rawget(i, 'error') or i.error == old_error then
|
flickerstreak@1
|
612 rawset(i, 'error', error)
|
flickerstreak@1
|
613 end
|
flickerstreak@1
|
614 if not rawget(i, 'assert') or i.assert == old_assert then
|
flickerstreak@1
|
615 rawset(i, 'assert', assert)
|
flickerstreak@1
|
616 end
|
flickerstreak@1
|
617 if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
|
flickerstreak@1
|
618 rawset(i, 'argCheck', argCheck)
|
flickerstreak@1
|
619 end
|
flickerstreak@1
|
620 if not rawget(i, 'pcall') or i.pcall == old_pcall then
|
flickerstreak@1
|
621 rawset(i, 'pcall', pcall)
|
flickerstreak@1
|
622 end
|
flickerstreak@1
|
623 end
|
flickerstreak@1
|
624 end
|
flickerstreak@1
|
625 end
|
flickerstreak@1
|
626 if activateFunc then
|
flickerstreak@1
|
627 safecall(activateFunc, instance, oldInstance, oldDeactivateFunc)
|
flickerstreak@1
|
628
|
flickerstreak@7
|
629 --[[ if major ~= ACELIBRARY_MAJOR then
|
flickerstreak@1
|
630 for k,v in pairs(_G) do
|
flickerstreak@1
|
631 if v == instance then
|
flickerstreak@7
|
632 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k))
|
flickerstreak@1
|
633 end
|
flickerstreak@1
|
634 end
|
flickerstreak@7
|
635 end]]
|
flickerstreak@1
|
636 else
|
flickerstreak@1
|
637 safecall(oldDeactivateFunc, oldInstance)
|
flickerstreak@1
|
638 end
|
flickerstreak@1
|
639 oldInstance = nil
|
flickerstreak@1
|
640
|
flickerstreak@1
|
641 if externalFunc then
|
flickerstreak@1
|
642 for k,data in pairs(self.libs) do
|
flickerstreak@1
|
643 if k ~= major then
|
flickerstreak@1
|
644 safecall(externalFunc, instance, k, data.instance)
|
flickerstreak@1
|
645 end
|
flickerstreak@1
|
646 end
|
flickerstreak@1
|
647 end
|
flickerstreak@1
|
648
|
flickerstreak@1
|
649 return instance
|
flickerstreak@1
|
650 end
|
flickerstreak@1
|
651
|
flickerstreak@1
|
652 local iter
|
flickerstreak@1
|
653 function AceLibrary:IterateLibraries()
|
flickerstreak@1
|
654 if not iter then
|
flickerstreak@1
|
655 local function iter(t, k)
|
flickerstreak@1
|
656 k = next(t, k)
|
flickerstreak@1
|
657 if not k then
|
flickerstreak@1
|
658 return nil
|
flickerstreak@1
|
659 else
|
flickerstreak@1
|
660 return k, t[k].instance
|
flickerstreak@1
|
661 end
|
flickerstreak@1
|
662 end
|
flickerstreak@1
|
663 end
|
flickerstreak@1
|
664 return iter, self.libs, nil
|
flickerstreak@1
|
665 end
|
flickerstreak@1
|
666
|
flickerstreak@1
|
667 -- @function Activate
|
flickerstreak@1
|
668 -- @brief The activateFunc for AceLibrary itself. Called when
|
flickerstreak@1
|
669 -- AceLibrary properly registers.
|
flickerstreak@1
|
670 -- @param self Reference to AceLibrary
|
flickerstreak@1
|
671 -- @param oldLib (optional) Reference to an old version of AceLibrary
|
flickerstreak@1
|
672 -- @param oldDeactivate (optional) Function to deactivate the old lib
|
flickerstreak@1
|
673 local function activate(self, oldLib, oldDeactivate)
|
flickerstreak@1
|
674 AceLibrary = self
|
flickerstreak@1
|
675 if not self.libs then
|
flickerstreak@1
|
676 self.libs = oldLib and oldLib.libs or {}
|
flickerstreak@1
|
677 self.scannedlibs = oldLib and oldLib.scannedlibs or {}
|
flickerstreak@1
|
678 end
|
flickerstreak@1
|
679 if not self.positions then
|
flickerstreak@1
|
680 self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" })
|
flickerstreak@1
|
681 end
|
flickerstreak@1
|
682
|
flickerstreak@1
|
683 -- Expose the library in the global environment
|
flickerstreak@1
|
684 _G[ACELIBRARY_MAJOR] = self
|
flickerstreak@1
|
685
|
flickerstreak@1
|
686 if oldDeactivate then
|
flickerstreak@1
|
687 oldDeactivate(oldLib)
|
flickerstreak@1
|
688 end
|
flickerstreak@1
|
689 end
|
flickerstreak@1
|
690
|
flickerstreak@1
|
691 if not previous then
|
flickerstreak@1
|
692 previous = AceLibrary
|
flickerstreak@1
|
693 end
|
flickerstreak@1
|
694 if not previous.libs then
|
flickerstreak@1
|
695 previous.libs = {}
|
flickerstreak@1
|
696 end
|
flickerstreak@1
|
697 AceLibrary.libs = previous.libs
|
flickerstreak@1
|
698 if not previous.positions then
|
flickerstreak@1
|
699 previous.positions = setmetatable({}, { __mode = "k" })
|
flickerstreak@1
|
700 end
|
flickerstreak@1
|
701 AceLibrary.positions = previous.positions
|
flickerstreak@1
|
702 AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)
|