mckenziemc@8
|
1 -- Tree
|
mckenziemc@8
|
2 -- Represents a recursive "tree" of addon
|
mckenziemc@8
|
3 -- dependencies rooted at a specific addon.
|
mckenziemc@8
|
4
|
mckenziemc@8
|
5
|
mckenziemc@8
|
6 local addonName, addonTable = ...
|
mckenziemc@8
|
7
|
mckenziemc@8
|
8
|
mckenziemc@10
|
9 -- FIXME: prevent infinite loops in the recursive functions
|
mckenziemc@8
|
10
|
mckenziemc@8
|
11
|
mckenziemc@8
|
12 local Tree, tree = addonTable:NewClass("Tree")
|
mckenziemc@8
|
13
|
mckenziemc@8
|
14
|
mckenziemc@18
|
15 local debug = addonTable.debug
|
mckenziemc@15
|
16 local classes = addonTable.classes
|
mckenziemc@15
|
17
|
mckenziemc@15
|
18
|
mckenziemc@8
|
19 Tree.trees = {}
|
mckenziemc@8
|
20
|
mckenziemc@10
|
21
|
mckenziemc@15
|
22 --- (private) Creates a new tree object.
|
mckenziemc@15
|
23 -- Assumptions: root addon exists and is not a Blizzard addon.
|
mckenziemc@8
|
24 -- @param root Name, index, or Addon object of the root addon.
|
mckenziemc@8
|
25 function Tree:New(root)
|
mckenziemc@8
|
26 if type(root) ~= "table" then
|
mckenziemc@15
|
27 root = classes.Addon:Get(root)
|
mckenziemc@8
|
28 end
|
mckenziemc@8
|
29
|
mckenziemc@8
|
30 local instance = {}
|
mckenziemc@8
|
31 setmetatable(instance, self.instanceMetatable)
|
mckenziemc@8
|
32
|
mckenziemc@8
|
33 instance.root = root
|
mckenziemc@8
|
34
|
mckenziemc@8
|
35 return instance
|
mckenziemc@8
|
36 end
|
mckenziemc@8
|
37
|
mckenziemc@8
|
38
|
mckenziemc@8
|
39 --- Retrieves the tree rooted at the specified addon
|
mckenziemc@15
|
40 -- Assumptions: root addon exists and is not a Blizzard addon
|
mckenziemc@8
|
41 -- @param root Name, index, or Addon object of the root.
|
mckenziemc@8
|
42 function Tree:Get(root)
|
mckenziemc@8
|
43 if type(root) ~= "table" then
|
mckenziemc@15
|
44 root = classes.Addon:Get(root)
|
mckenziemc@8
|
45 end
|
mckenziemc@8
|
46
|
mckenziemc@8
|
47 local tree = self.trees[root]
|
mckenziemc@8
|
48
|
mckenziemc@8
|
49 if tree then
|
mckenziemc@8
|
50 return tree
|
mckenziemc@8
|
51 else
|
mckenziemc@8
|
52 tree = self:New(root)
|
mckenziemc@8
|
53 self.trees[root] = tree
|
mckenziemc@8
|
54 return tree
|
mckenziemc@8
|
55 end
|
mckenziemc@8
|
56 end
|
mckenziemc@8
|
57
|
mckenziemc@8
|
58
|
mckenziemc@15
|
59 -- NOTE: All tree:Can functions should check the root too.
|
mckenziemc@15
|
60 -- That way, the hooks don't have to do "if addon:Can_ and tree:Can_",
|
mckenziemc@15
|
61 -- plus the tree can cache info that includes the capabilities of the
|
mckenziemc@15
|
62 -- root addon.
|
mckenziemc@15
|
63
|
mckenziemc@15
|
64
|
mckenziemc@15
|
65 --- Checks if the tree rooted at the specified addon
|
mckenziemc@12
|
66 -- can be loaded if all dependencies are enabled
|
mckenziemc@12
|
67 -- and the UI is reloaded.
|
mckenziemc@12
|
68 -- Basically makes sure all dependencies are installed.
|
mckenziemc@12
|
69 function tree:CanLoad()
|
mckenziemc@15
|
70 local root = self.root
|
mckenziemc@15
|
71
|
mckenziemc@18
|
72 --debug("Checking if the tree rooted at", root:GetName(), "can be loaded.")
|
mckenziemc@18
|
73
|
mckenziemc@15
|
74 if root:IsLoaded() then
|
mckenziemc@15
|
75 return true
|
mckenziemc@15
|
76 end
|
mckenziemc@15
|
77
|
mckenziemc@15
|
78 if not root:CanLoad() then
|
mckenziemc@12
|
79 return false
|
mckenziemc@12
|
80 end
|
mckenziemc@12
|
81
|
mckenziemc@12
|
82 -- check all the dependencies
|
mckenziemc@15
|
83 local dependencies = {root:GetDependencies()}
|
mckenziemc@15
|
84
|
mckenziemc@8
|
85 for i, depName in pairs(dependencies) do
|
mckenziemc@15
|
86 if not classes.Addon:Exists(depName) then
|
mckenziemc@12
|
87 return false
|
mckenziemc@12
|
88 end
|
mckenziemc@12
|
89
|
mckenziemc@15
|
90 local dep = classes.Addon:Get(depName)
|
mckenziemc@15
|
91 local depTree = Tree:Get(dep)
|
mckenziemc@8
|
92
|
mckenziemc@12
|
93 if not depTree:CanLoad() then
|
mckenziemc@15
|
94 return false
|
mckenziemc@10
|
95 end
|
mckenziemc@10
|
96 end
|
mckenziemc@10
|
97
|
mckenziemc@18
|
98 --debug("The tree rooted at", root:GetName(), "can be loaded")
|
mckenziemc@12
|
99 return true
|
mckenziemc@8
|
100 end
|
mckenziemc@8
|
101
|
mckenziemc@8
|
102
|
mckenziemc@12
|
103 --- Checks if the tree can be loaded on demand.
|
mckenziemc@15
|
104 -- Does not allow for force-loading; dependencies must
|
mckenziemc@15
|
105 -- already be loaded, or enabled and LoD.
|
mckenziemc@10
|
106 function tree:CanLoD()
|
mckenziemc@15
|
107 local root = self.root
|
mckenziemc@15
|
108
|
mckenziemc@15
|
109 if root:IsLoaded() then
|
mckenziemc@8
|
110 return true
|
mckenziemc@8
|
111 end
|
mckenziemc@8
|
112
|
mckenziemc@15
|
113 if not root:CanLoD() then
|
mckenziemc@8
|
114 return false
|
mckenziemc@8
|
115 end
|
mckenziemc@8
|
116
|
mckenziemc@8
|
117 -- now check dependencies recursively
|
mckenziemc@15
|
118 local dependencies = {root:GetDependencies()}
|
mckenziemc@15
|
119
|
mckenziemc@8
|
120 for i, depName in pairs(dependencies) do
|
mckenziemc@15
|
121 if not classes.Addon:Exists(depName) then
|
mckenziemc@15
|
122 return false
|
mckenziemc@15
|
123 end
|
mckenziemc@15
|
124
|
mckenziemc@15
|
125 local dep = classes.Addon:Get(depName)
|
mckenziemc@15
|
126 local depTree = Tree:Get(dep)
|
mckenziemc@8
|
127
|
mckenziemc@10
|
128 if not depTree:CanLoD() then
|
mckenziemc@10
|
129 return false
|
mckenziemc@8
|
130 end
|
mckenziemc@8
|
131 end
|
mckenziemc@8
|
132
|
mckenziemc@8
|
133 return true
|
mckenziemc@8
|
134 end
|
mckenziemc@8
|
135
|
mckenziemc@8
|
136
|
mckenziemc@18
|
137 -- Checks if this tree can load late (LoadAddOn with non-LoD)
|
mckenziemc@18
|
138 function tree:CanLoadLate()
|
mckenziemc@18
|
139 local root = self.root
|
mckenziemc@18
|
140
|
mckenziemc@18
|
141 if not root:CanLoadLate() then
|
mckenziemc@18
|
142 return false
|
mckenziemc@18
|
143 end
|
mckenziemc@18
|
144
|
mckenziemc@18
|
145 local dependencies = {root:GetDependencies()}
|
mckenziemc@18
|
146
|
mckenziemc@18
|
147 for i, depName in pairs(dependencies) do
|
mckenziemc@18
|
148 if not classes.Addon:Exists(depName) then
|
mckenziemc@18
|
149 return false
|
mckenziemc@18
|
150 end
|
mckenziemc@18
|
151
|
mckenziemc@18
|
152 local dep = classes.Addon:Get(depName)
|
mckenziemc@18
|
153 local depTree = Tree:Get(dep)
|
mckenziemc@18
|
154
|
mckenziemc@18
|
155 if not depTree:CanLoadLate() then
|
mckenziemc@18
|
156 return false
|
mckenziemc@18
|
157 end
|
mckenziemc@18
|
158 end
|
mckenziemc@18
|
159
|
mckenziemc@18
|
160 return true
|
mckenziemc@18
|
161 end
|
mckenziemc@18
|
162
|
mckenziemc@18
|
163
|
mckenziemc@15
|
164 --- Checks if this tree can be force-loaded.
|
mckenziemc@15
|
165 -- Does not check user settings nor if force-loading is actually available.
|
mckenziemc@15
|
166 -- @return canForceLoad True if this tree can be force loaded, false otherwise
|
mckenziemc@15
|
167 function tree:CanForceLoad()
|
mckenziemc@15
|
168 local root = self.root
|
mckenziemc@15
|
169 -- TODO: remove redundencies (for now they're here for design flexibility)
|
mckenziemc@15
|
170
|
mckenziemc@15
|
171 -- this addon must be loaded, able to load-on-demand, or able to force-load
|
mckenziemc@15
|
172 if root:IsLoaded() then
|
mckenziemc@15
|
173 return true
|
mckenziemc@15
|
174 end
|
mckenziemc@15
|
175
|
mckenziemc@15
|
176 if not root:CanForceLoad() then
|
mckenziemc@12
|
177 return false
|
mckenziemc@8
|
178 end
|
mckenziemc@8
|
179
|
mckenziemc@8
|
180 -- now check dependencies recursively
|
mckenziemc@15
|
181 local dependencies = {root:GetDependencies()}
|
mckenziemc@15
|
182
|
mckenziemc@8
|
183 for i, depName in pairs(dependencies) do
|
mckenziemc@15
|
184 if not classes.Addon:Exists(depName) then
|
mckenziemc@12
|
185 return false
|
mckenziemc@8
|
186 end
|
mckenziemc@12
|
187
|
mckenziemc@15
|
188 local dep = classes.Addon:Get(depName)
|
mckenziemc@15
|
189 local depTree = Tree:Get(dep)
|
mckenziemc@15
|
190
|
mckenziemc@15
|
191 if not depTree:CanForceLoad() then
|
mckenziemc@15
|
192 return false
|
mckenziemc@12
|
193 end
|
mckenziemc@8
|
194 end
|
mckenziemc@8
|
195
|
mckenziemc@8
|
196 return true
|
mckenziemc@8
|
197 end
|
mckenziemc@8
|
198
|
mckenziemc@12
|
199
|
mckenziemc@15
|
200 --- Prepares this tree to be loaded, whether through
|
mckenziemc@15
|
201 -- a ui reload or by loading on demand.
|
mckenziemc@15
|
202 -- Assumptions: CanLoad is true for this tree
|
mckenziemc@15
|
203 function tree:PrepareForLoad()
|
mckenziemc@15
|
204 local root = self.root
|
mckenziemc@15
|
205
|
mckenziemc@18
|
206 --debug("Preparing the tree rooted at", root:GetName(), "for loading")
|
mckenziemc@18
|
207
|
mckenziemc@15
|
208 -- The Addon class will take care of delaying enables
|
mckenziemc@15
|
209 -- till after PLAYER_LOGIN if necessary.
|
mckenziemc@15
|
210
|
mckenziemc@15
|
211 root:Enable()
|
mckenziemc@15
|
212
|
mckenziemc@15
|
213 -- enable dependencies
|
mckenziemc@15
|
214 local dependencies = {root:GetDependencies()}
|
mckenziemc@15
|
215
|
mckenziemc@15
|
216 for i, depName in pairs(dependencies) do
|
mckenziemc@15
|
217 Tree:Get(depName):PrepareForLoad()
|
mckenziemc@15
|
218 end
|
mckenziemc@12
|
219
|
mckenziemc@15
|
220 -- prepare embeds, if they are available separately
|
mckenziemc@15
|
221 local embeds = {root:GetEmbeds(addon)}
|
mckenziemc@12
|
222
|
mckenziemc@15
|
223 for i, embedName in pairs(embeds) do
|
mckenziemc@15
|
224 if classes.Addon:Exists(embedName) then
|
mckenziemc@15
|
225 Tree:Get(embedName):PrepareForLoad()
|
mckenziemc@12
|
226 end
|
mckenziemc@12
|
227 end
|
mckenziemc@12
|
228 end
|
mckenziemc@12
|
229
|
mckenziemc@12
|
230
|
mckenziemc@12
|
231 -- I think the problem this solves is a major issue with
|
mckenziemc@12
|
232 -- migrating to separate libs. think about it more and document
|
mckenziemc@12
|
233 -- here and in project description
|
mckenziemc@15
|
234
|
mckenziemc@15
|
235 --- Force-loads this addon tree. If a subtree can be loaded on
|
mckenziemc@15
|
236 -- demand, this function will enable but not load the subtree.
|
mckenziemc@15
|
237 -- Assumptions: the root addon should be force-loaded, not just enabled.
|
mckenziemc@15
|
238 --function tree:ForceLoad()
|
mckenziemc@15
|
239
|
mckenziemc@15
|
240 -- Assumptions: the root is loadable-on demand, and force-load is available.
|
mckenziemc@15
|
241 -- and tree can be force-loaded
|
mckenziemc@12
|
242 function tree:PrepareForLoD()
|
mckenziemc@15
|
243 local root = self.root
|
mckenziemc@12
|
244
|
mckenziemc@15
|
245 -- prepare dependencies first (for consistency)
|
mckenziemc@15
|
246 local dependencies = {root:GetDependencies()}
|
mckenziemc@15
|
247
|
mckenziemc@12
|
248 for i, depName in pairs(dependencies) do
|
mckenziemc@15
|
249 local dep = classes.Addon:Get(depName)
|
mckenziemc@12
|
250
|
mckenziemc@15
|
251 if not dep:IsLoaded() then
|
mckenziemc@15
|
252 if dep:CanLoD() then
|
mckenziemc@15
|
253 -- don't load it now but make sure its dependencies are prepared
|
mckenziemc@15
|
254 Tree:Get(dep):PrepareForLoD()
|
mckenziemc@15
|
255 else
|
mckenziemc@15
|
256 -- Based on our assumption about the tree, this addon
|
mckenziemc@15
|
257 -- should be able to force-load.
|
mckenziemc@15
|
258 -- force-load it now so we can load the parent on demand
|
mckenziemc@15
|
259 Tree:Get(dep):ForceLoad()
|
mckenziemc@15
|
260 end
|
mckenziemc@12
|
261 end
|
mckenziemc@12
|
262 end
|
mckenziemc@12
|
263
|
mckenziemc@12
|
264 -- prepare embeds, if they are available separately
|
mckenziemc@15
|
265 local embeds = {root:GetEmbeds()}
|
mckenziemc@15
|
266
|
mckenziemc@12
|
267 for i, embedName in pairs(embeds) do
|
mckenziemc@15
|
268 if classes.Addon:Exists(embedName) then
|
mckenziemc@15
|
269 local embed = classes.Addon:Get(embedName)
|
mckenziemc@15
|
270
|
mckenziemc@15
|
271 if not embed:IsLoaded() then
|
mckenziemc@15
|
272 if embed:CanLoD() then
|
mckenziemc@15
|
273 Tree:Get(embed):PrepareForLoD()
|
mckenziemc@15
|
274 else
|
mckenziemc@15
|
275 Tree:Get(embed):ForceLoad()
|
mckenziemc@15
|
276 end
|
mckenziemc@12
|
277 end
|
mckenziemc@12
|
278 end
|
mckenziemc@12
|
279 end
|
mckenziemc@15
|
280
|
mckenziemc@15
|
281 root:Enable()
|
mckenziemc@12
|
282 end
|
mckenziemc@12
|
283
|
mckenziemc@12
|
284
|
mckenziemc@18
|
285 function tree:LoadLate()
|
mckenziemc@18
|
286 local root = self.root
|
mckenziemc@18
|
287
|
mckenziemc@18
|
288 -- load dependencies
|
mckenziemc@18
|
289 local dependencies = {root:GetDependencies()}
|
mckenziemc@18
|
290
|
mckenziemc@18
|
291 for i, depName in pairs(dependencies) do
|
mckenziemc@18
|
292 Tree:Get(depName):LoadLate()
|
mckenziemc@18
|
293 end
|
mckenziemc@18
|
294
|
mckenziemc@18
|
295 -- load embeds, if they are available separately
|
mckenziemc@18
|
296 local embeds = {root:GetEmbeds()}
|
mckenziemc@18
|
297
|
mckenziemc@18
|
298 for i, embedName in pairs(embeds) do
|
mckenziemc@18
|
299 if classes.Addon:Exists(embedName) then
|
mckenziemc@18
|
300 Tree:Get(embedName):LoadLate()
|
mckenziemc@18
|
301 end
|
mckenziemc@18
|
302 end
|
mckenziemc@18
|
303
|
mckenziemc@18
|
304 root:LoadLate()
|
mckenziemc@18
|
305 end
|
mckenziemc@18
|
306
|
mckenziemc@18
|
307
|
mckenziemc@18
|
308
|
mckenziemc@15
|
309 --- Force-loads this tree.
|
mckenziemc@15
|
310 -- This will also load any LoD addons in the tree
|
mckenziemc@10
|
311 function tree:ForceLoad()
|
mckenziemc@15
|
312 local root = self.root
|
mckenziemc@15
|
313
|
mckenziemc@15
|
314 -- FIXME: if already loaded
|
mckenziemc@15
|
315
|
mckenziemc@8
|
316 -- load dependencies
|
mckenziemc@15
|
317 local dependencies = {root:GetDependencies()}
|
mckenziemc@15
|
318
|
mckenziemc@8
|
319 for i, depName in pairs(dependencies) do
|
mckenziemc@15
|
320 Tree:Get(depName):ForceLoad()
|
mckenziemc@8
|
321 end
|
mckenziemc@8
|
322
|
mckenziemc@8
|
323 -- load embeds, if they are available separately
|
mckenziemc@15
|
324 local embeds = {root:GetEmbeds()}
|
mckenziemc@15
|
325
|
mckenziemc@8
|
326 for i, embedName in pairs(embeds) do
|
mckenziemc@15
|
327 if classes.Addon:Exists(embedName) then
|
mckenziemc@15
|
328 Tree:Get(embedName):ForceLoad()
|
mckenziemc@15
|
329 end
|
mckenziemc@8
|
330 end
|
mckenziemc@8
|
331
|
mckenziemc@18
|
332 if root:CanLoD() then
|
mckenziemc@18
|
333 root:Load()
|
mckenziemc@18
|
334 else
|
mckenziemc@18
|
335 root:ForceLoad()
|
mckenziemc@18
|
336 end
|
mckenziemc@8
|
337 end
|