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@8
|
9 -- TODO: 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@8
|
15 Tree.trees = {}
|
mckenziemc@8
|
16
|
mckenziemc@8
|
17 -- internal
|
mckenziemc@8
|
18 -- Creates a new tree object
|
mckenziemc@8
|
19 -- @param root Name, index, or Addon object of the root addon.
|
mckenziemc@8
|
20 function Tree:New(root)
|
mckenziemc@8
|
21 if type(root) ~= "table" then
|
mckenziemc@8
|
22 root = addonTable.classes.Addon:Get(root)
|
mckenziemc@8
|
23 end
|
mckenziemc@8
|
24
|
mckenziemc@8
|
25 local instance = {}
|
mckenziemc@8
|
26 setmetatable(instance, self.instanceMetatable)
|
mckenziemc@8
|
27
|
mckenziemc@8
|
28 instance.root = root
|
mckenziemc@8
|
29
|
mckenziemc@8
|
30 return instance
|
mckenziemc@8
|
31 end
|
mckenziemc@8
|
32
|
mckenziemc@8
|
33
|
mckenziemc@8
|
34 --- Retrieves the tree rooted at the specified addon
|
mckenziemc@8
|
35 -- @param root Name, index, or Addon object of the root.
|
mckenziemc@8
|
36 function Tree:Get(root)
|
mckenziemc@8
|
37 if type(root) ~= "table" then
|
mckenziemc@8
|
38 root = addonTable.classes.Addon:Get(root)
|
mckenziemc@8
|
39 end
|
mckenziemc@8
|
40
|
mckenziemc@8
|
41 local tree = self.trees[root]
|
mckenziemc@8
|
42
|
mckenziemc@8
|
43 if tree then
|
mckenziemc@8
|
44 return tree
|
mckenziemc@8
|
45 else
|
mckenziemc@8
|
46 tree = self:New(root)
|
mckenziemc@8
|
47 self.trees[root] = tree
|
mckenziemc@8
|
48 return tree
|
mckenziemc@8
|
49 end
|
mckenziemc@8
|
50 end
|
mckenziemc@8
|
51
|
mckenziemc@8
|
52
|
mckenziemc@8
|
53 function tree:GetLoadBitfield()
|
mckenziemc@8
|
54 local bitfield = self.root:GetLoadBitfield()
|
mckenziemc@8
|
55
|
mckenziemc@8
|
56 local dependencies = {self.root:GetDependencies()}
|
mckenziemc@8
|
57 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
58 local depTree = Tree:Get(depName)
|
mckenziemc@8
|
59
|
mckenziemc@8
|
60 if depTree == nil then
|
mckenziemc@8
|
61 return 0
|
mckenziemc@8
|
62 end
|
mckenziemc@8
|
63
|
mckenziemc@8
|
64 bitfield = bit.band(bitfield, depTree:GetLoadBitfield())
|
mckenziemc@8
|
65 end
|
mckenziemc@8
|
66
|
mckenziemc@8
|
67 return bitfield
|
mckenziemc@8
|
68 end
|
mckenziemc@8
|
69
|
mckenziemc@8
|
70
|
mckenziemc@8
|
71 -- Checks if the tree rooted at the specified addon can be force-loaded.
|
mckenziemc@8
|
72 -- @param root The name or index of the root addon.
|
mckenziemc@8
|
73 function core:CanForceLoadTree(root)
|
mckenziemc@8
|
74 -- TODO: if some addons have already loaded, we have to check
|
mckenziemc@8
|
75 -- forceafter for those and forcebefore for the others
|
mckenziemc@8
|
76 return false
|
mckenziemc@8
|
77 end
|
mckenziemc@8
|
78
|
mckenziemc@8
|
79
|
mckenziemc@8
|
80 -- NOTE: any tree that can be loaded on demand is also eligible for force-loading
|
mckenziemc@8
|
81 -- Checks if the tree rooted at the specified addon can be loaded on demand.
|
mckenziemc@8
|
82 -- @param root The name or index of the root addon.
|
mckenziemc@8
|
83 function core:CanLoDTree(root)
|
mckenziemc@8
|
84 local bitfield = self:GetLoadBitfield()
|
mckenziemc@8
|
85 root = self:GetAddon(root)
|
mckenziemc@8
|
86 assert(root)
|
mckenziemc@8
|
87
|
mckenziemc@8
|
88 -- since this will be used recursively, return true if
|
mckenziemc@8
|
89 -- this is already loaded.
|
mckenziemc@8
|
90 if root:IsLoaded() then
|
mckenziemc@8
|
91 return true
|
mckenziemc@8
|
92 end
|
mckenziemc@8
|
93
|
mckenziemc@8
|
94 -- true if all dependencies are loaded or LoD
|
mckenziemc@8
|
95
|
mckenziemc@8
|
96 if not root:CanLoD() then
|
mckenziemc@8
|
97 return false
|
mckenziemc@8
|
98 end
|
mckenziemc@8
|
99
|
mckenziemc@8
|
100 -- now check dependencies recursively
|
mckenziemc@8
|
101 local dependencies = {root:GetDependencies()}
|
mckenziemc@8
|
102 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
103 local dep = self:GetAddon(depName)
|
mckenziemc@8
|
104
|
mckenziemc@8
|
105 if not dep then
|
mckenziemc@8
|
106 return false -- missing
|
mckenziemc@8
|
107 end
|
mckenziemc@8
|
108
|
mckenziemc@8
|
109 if not dep:IsLoaded() then
|
mckenziemc@8
|
110 if not self:CanLoDTree(depName) then
|
mckenziemc@8
|
111 return false
|
mckenziemc@8
|
112 end
|
mckenziemc@8
|
113 end
|
mckenziemc@8
|
114 end
|
mckenziemc@8
|
115
|
mckenziemc@8
|
116 return true
|
mckenziemc@8
|
117 end
|
mckenziemc@8
|
118
|
mckenziemc@8
|
119
|
mckenziemc@8
|
120 -- Checks if the tree rooted at the specified addon
|
mckenziemc@8
|
121 -- can be loaded if all dependencies are enabled
|
mckenziemc@8
|
122 -- the UI is reloaded.
|
mckenziemc@8
|
123 -- Basically makes sure all dependencies are installed.
|
mckenziemc@8
|
124 function core:CanReloadTree(root)
|
mckenziemc@8
|
125 -- convert root to an Addon object
|
mckenziemc@8
|
126 root = self:GetAddon(root)
|
mckenziemc@8
|
127 assert(root)
|
mckenziemc@8
|
128
|
mckenziemc@8
|
129 if root:IsLoaded() then
|
mckenziemc@8
|
130 return true
|
mckenziemc@8
|
131 -- FIXME: deps may have been disabled
|
mckenziemc@8
|
132 end
|
mckenziemc@8
|
133
|
mckenziemc@8
|
134 -- TODO: make sure the root isn't incompatible
|
mckenziemc@8
|
135
|
mckenziemc@8
|
136 -- now check dependencies recursively
|
mckenziemc@8
|
137 -- FIXME: prevent infinite recursion
|
mckenziemc@8
|
138
|
mckenziemc@8
|
139 local dependencies = {root:GetDependencies()}
|
mckenziemc@8
|
140 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
141 local dep = self:GetAddon(depName)
|
mckenziemc@8
|
142
|
mckenziemc@8
|
143 if dep == nil then
|
mckenziemc@8
|
144 return false -- missing dependency
|
mckenziemc@8
|
145 end
|
mckenziemc@8
|
146
|
mckenziemc@8
|
147 -- TODO: make sure it's compatible
|
mckenziemc@8
|
148 end
|
mckenziemc@8
|
149
|
mckenziemc@8
|
150 return true
|
mckenziemc@8
|
151 end
|
mckenziemc@8
|
152
|
mckenziemc@8
|
153 -- Loads the tree rooted at the specified addon.
|
mckenziemc@8
|
154 -- FIXME: load the root too or not?
|
mckenziemc@8
|
155 -- Supports both LoD addons and those that require force-loading.
|
mckenziemc@8
|
156 function core:LoadTree(addon)
|
mckenziemc@8
|
157 -- don't check if the tree can actually be loaded.
|
mckenziemc@8
|
158 -- external code should do that itself to check if it
|
mckenziemc@8
|
159 -- should even call this at all.
|
mckenziemc@8
|
160
|
mckenziemc@8
|
161 if self:ForceLoadAvailable() then
|
mckenziemc@8
|
162 -- LoD trees can also be force-loaded
|
mckenziemc@8
|
163 self:ForceLoadTree(addon)
|
mckenziemc@8
|
164 else
|
mckenziemc@8
|
165 self:LoadLoDTree(addon)
|
mckenziemc@8
|
166 end
|
mckenziemc@8
|
167 end
|
mckenziemc@8
|
168
|
mckenziemc@8
|
169
|
mckenziemc@8
|
170 -- load the root too, since it may actually be a leaf
|
mckenziemc@8
|
171 function core:ForceLoadTree(root)
|
mckenziemc@8
|
172 root = self:GetAddon(root)
|
mckenziemc@8
|
173 assert(root)
|
mckenziemc@8
|
174
|
mckenziemc@8
|
175 -- load dependencies
|
mckenziemc@8
|
176 local dependencies = {root:GetDependencies(addon)}
|
mckenziemc@8
|
177 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
178 self:ForceLoadTree(depName)
|
mckenziemc@8
|
179 end
|
mckenziemc@8
|
180
|
mckenziemc@8
|
181 -- load embeds, if they are available separately
|
mckenziemc@8
|
182 local embeds = {root:GetEmbeds(addon)}
|
mckenziemc@8
|
183 for i, embedName in pairs(embeds) do
|
mckenziemc@8
|
184 if Addon:Exists(embedName) then
|
mckenziemc@8
|
185 self:ForceLoadTree(embedName)
|
mckenziemc@8
|
186 end
|
mckenziemc@8
|
187 end
|
mckenziemc@8
|
188
|
mckenziemc@8
|
189 root:ForceLoad()
|
mckenziemc@8
|
190 end
|
mckenziemc@8
|
191
|
mckenziemc@8
|
192
|
mckenziemc@8
|
193 function core:LoadLoDTree(root)
|
mckenziemc@8
|
194 root = self:GetAddon(root)
|
mckenziemc@8
|
195 assert(root)
|
mckenziemc@8
|
196
|
mckenziemc@8
|
197 -- load dependencies
|
mckenziemc@8
|
198 local dependencies = {root:GetDependencies(addon)}
|
mckenziemc@8
|
199 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
200 self:LoadLoDTree(depName)
|
mckenziemc@8
|
201 end
|
mckenziemc@8
|
202
|
mckenziemc@8
|
203 -- load embeds, if they are available separately
|
mckenziemc@8
|
204 local embeds = {root:GetEmbeds(addon)}
|
mckenziemc@8
|
205 for i, embedName in pairs(embeds) do
|
mckenziemc@8
|
206 if Addon:Exists(embedName) then
|
mckenziemc@8
|
207 self:LoadLoDTree(embedName)
|
mckenziemc@8
|
208 end
|
mckenziemc@8
|
209 end
|
mckenziemc@8
|
210
|
mckenziemc@8
|
211 root:LoD()
|
mckenziemc@8
|
212 end
|
mckenziemc@8
|
213
|
mckenziemc@8
|
214
|
mckenziemc@8
|
215 -- I think the problem this solves is a major issue with
|
mckenziemc@8
|
216 -- migrating to separate libs. think about it more and document
|
mckenziemc@8
|
217 -- here and in project description
|
mckenziemc@8
|
218 function core:PrepareLoDTree(root)
|
mckenziemc@8
|
219 root = self:GetAddon(root)
|
mckenziemc@8
|
220 assert(root)
|
mckenziemc@8
|
221
|
mckenziemc@8
|
222 -- assume root is LoD
|
mckenziemc@8
|
223
|
mckenziemc@8
|
224 -- check dependencies
|
mckenziemc@8
|
225 local dependencies = {root:GetDependencies(addon)}
|
mckenziemc@8
|
226 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
227 local dep = self:GetAddon(depName)
|
mckenziemc@8
|
228
|
mckenziemc@8
|
229 -- assume external code made sure it exists
|
mckenziemc@8
|
230
|
mckenziemc@8
|
231 if dep:CanLoD() then
|
mckenziemc@8
|
232 -- don't load it now but make sure its dependencies are prepared
|
mckenziemc@8
|
233 self:PrepareLoDTree(depName)
|
mckenziemc@8
|
234 else
|
mckenziemc@8
|
235 -- FIXME: if it's already loaded
|
mckenziemc@8
|
236 -- force-load it now so we can load the parent on demand
|
mckenziemc@8
|
237 self:ForceLoadTree(depName)
|
mckenziemc@8
|
238 end
|
mckenziemc@8
|
239 end
|
mckenziemc@8
|
240
|
mckenziemc@8
|
241 -- prepare embeds, if they are available separately
|
mckenziemc@8
|
242 local embeds = {root:GetEmbeds(addon)} -- FIXME: addon?
|
mckenziemc@8
|
243 for i, embedName in pairs(embeds) do
|
mckenziemc@8
|
244 local embed = self:GetAddon(embedName)
|
mckenziemc@8
|
245
|
mckenziemc@8
|
246 if embed then
|
mckenziemc@8
|
247 if embed:CanLoD() then
|
mckenziemc@8
|
248 -- don't load it now but make sure its dependencies are prepared
|
mckenziemc@8
|
249 self:PrepareLoDTree(embedName)
|
mckenziemc@8
|
250 else
|
mckenziemc@8
|
251 -- FIXME: if it's already loaded
|
mckenziemc@8
|
252 -- force-load it now so we can load the parent on demand
|
mckenziemc@8
|
253 self:ForceLoadTree(depName)
|
mckenziemc@8
|
254 end
|
mckenziemc@8
|
255 end
|
mckenziemc@8
|
256 end
|
mckenziemc@8
|
257 end
|
mckenziemc@8
|
258
|
mckenziemc@8
|
259
|
mckenziemc@8
|
260 function Core:PrepareReloadTree(addon)
|
mckenziemc@8
|
261 root = self:GetAddon(root)
|
mckenziemc@8
|
262 assert(root)
|
mckenziemc@8
|
263
|
mckenziemc@8
|
264 root:Enable()
|
mckenziemc@8
|
265
|
mckenziemc@8
|
266 -- check dependencies
|
mckenziemc@8
|
267 local dependencies = {root:GetDependencies()}
|
mckenziemc@8
|
268 for i, depName in pairs(dependencies) do
|
mckenziemc@8
|
269 self:PrepareReloadTree(depName)
|
mckenziemc@8
|
270 end
|
mckenziemc@8
|
271
|
mckenziemc@8
|
272 -- prepare embeds, if they are available separately
|
mckenziemc@8
|
273 local embeds = {root:GetEmbeds(addon)}
|
mckenziemc@8
|
274 for i, embedName in pairs(embeds) do
|
mckenziemc@8
|
275 local embed = self:GetAddon(embedName)
|
mckenziemc@8
|
276
|
mckenziemc@8
|
277 if embed then
|
mckenziemc@8
|
278 self:PrepareReloadTree(embedName)
|
mckenziemc@8
|
279 end
|
mckenziemc@8
|
280 end
|
mckenziemc@8
|
281 end
|
mckenziemc@8
|
282
|
mckenziemc@8
|
283
|
mckenziemc@8
|
284 function Core:ForceLoadAvailable()
|
mckenziemc@8
|
285 return true
|
mckenziemc@8
|
286 -- FIXME: use field and a frame registered for PLAYER_LOGIN
|
mckenziemc@8
|
287 end
|