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