PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/LibPeriodicTable-3.1/LibPeriodicTable-3.1/LibPeriodicTable-3.1.lua

https://code.google.com/p/strongcwowinterface/
Lua | 371 lines | 269 code | 20 blank | 82 comment | 73 complexity | 9226bc7ab7a5256e35fe0dd15e6fa8ce MD5 | raw file
Possible License(s): Unlicense, LGPL-3.0, LGPL-2.1, BSD-3-Clause, GPL-2.0
  1. --[[
  2. Name: PeriodicTable-3.1
  3. Revision: $Rev: 6 $
  4. Author: Nymbia (nymbia@gmail.com)
  5. Many thanks to Tekkub for writing PeriodicTable 1 and 2, and for permission to use the name PeriodicTable!
  6. Website: http://www.wowace.com/wiki/PeriodicTable-3.1
  7. Documentation: http://www.wowace.com/wiki/PeriodicTable-3.1/API
  8. SVN: http://svn.wowace.com/wowace/trunk/PeriodicTable-3.1/PeriodicTable-3.1/
  9. Description: Library of compressed itemid sets.
  10. Dependencies: AceLibrary
  11. License: LGPL v2.1
  12. Copyright (C) 2007 Nymbia
  13. This library is free software; you can redistribute it and/or
  14. modify it under the terms of the GNU Lesser General Public
  15. License as published by the Free Software Foundation; either
  16. version 2.1 of the License, or (at your option) any later version.
  17. This library is distributed in the hope that it will be useful,
  18. but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. Lesser General Public License for more details.
  21. You should have received a copy of the GNU Lesser General Public
  22. License along with this library; if not, write to the Free Software
  23. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. ]]
  25. local PT3, oldminor = LibStub:NewLibrary("LibPeriodicTable-3.1", tonumber(("$Revision: 6 $"):match("(%d+)")) + 90000)
  26. if not PT3 then
  27. return
  28. end
  29. -- local references to oft-used global functions.
  30. local type = type
  31. local rawget = rawget
  32. local tonumber = tonumber
  33. local pairs = pairs
  34. local ipairs = ipairs
  35. local next = next
  36. local assert = assert
  37. local table_concat = table.concat
  38. local iternum, iterpos, cache, sets, embedversions
  39. ---------------------------------------------
  40. -- Internal / Local Functions --
  41. ---------------------------------------------
  42. local getItemID, makeNonPresentMultiSet, shredCache, setiter, multisetiter
  43. function getItemID(item)
  44. -- accepts either an item string ie "item:12345:0:0:0:2342:123324:12:1", hyperlink, or an itemid.
  45. -- returns a number'ified itemid.
  46. return tonumber(item) or tonumber(item:match("item:(%d+)")) or (-1 * ((item:match("enchant:(%d+)") or item:match("spell:(%d+)")) or 0))
  47. end
  48. do
  49. local tables = setmetatable({},{__mode = 'k'})
  50. function makeNonPresentMultiSet(parentname)
  51. -- makes an implied multiset, ie if you define only the set "a.b.c",
  52. -- a request to "a.b" will come through here for a.b to be built.
  53. -- an expensive function because it needs to iterate all active sets,
  54. -- moreso for invalid sets.
  55. -- store some temp tables with weak keys to reduce garbage churn
  56. local temp = next(tables)
  57. if temp then
  58. tables[temp] = nil
  59. else
  60. temp = {}
  61. end
  62. -- Escape characters that will screw up the name matching.
  63. local escapedparentname = parentname:gsub("([%.%(%)%%%+%-%*%?%[%]%^%$])", "%%%1")
  64. -- Check all the sets to see if they start with this name.
  65. for k in pairs(sets) do
  66. if k:match("^"..escapedparentname.."%.") then
  67. temp[#temp+1] = k
  68. end
  69. end
  70. if #temp == 0 then
  71. sets[parentname] = false
  72. else
  73. sets[parentname] = "m,"..table_concat(temp, ',')
  74. end
  75. -- clear the temp table then feed it back into the recycler
  76. for k in pairs(temp) do
  77. temp[k] = nil
  78. end
  79. tables[temp] = true
  80. end
  81. end
  82. function shredCache(setname)
  83. -- If there's a cache for this set, delete it, since we just added a new copy.
  84. if rawget(cache, setname) then
  85. cache[setname] = nil
  86. end
  87. local parentname = setname:match("^(.+)%.[^%.]+$")
  88. if parentname then
  89. -- Recurse and do the same for the parent set if we find one.
  90. shredCache(parentname)
  91. end
  92. end
  93. function setiter(t)
  94. local k,v
  95. if iterpos then
  96. -- We already have a position that we're at in the iteration, grab the next value up.
  97. k,v = next(t,iterpos)
  98. else
  99. -- We havent yet touched this set, grab the first value.
  100. k,v = next(t)
  101. end
  102. if k == "set" then
  103. k,v = next(t, k)
  104. end
  105. if k then
  106. iterpos = k
  107. return k,v,t.set
  108. end
  109. end
  110. function multisetiter(t)
  111. local k,v
  112. if iterpos then
  113. -- We already have a position that we're at in the iteration, grab the next value up.
  114. k,v = next(t[iternum],iterpos)
  115. else
  116. -- We havent yet touched this set, grab the first value.
  117. k,v = next(t[iternum])
  118. end
  119. if k == "set" then
  120. k,v = next(t[iternum], k)
  121. end
  122. if k then
  123. -- There's an entry here, no need to move on to the next table yet.
  124. iterpos = k
  125. return k,v,t[iternum].set
  126. else
  127. -- No entry, time to check for a new table.
  128. iternum = iternum + 1
  129. if not t[iternum] then
  130. return
  131. end
  132. k,v = next(t[iternum])
  133. if k == "set" then
  134. k,v = next(t[iternum],k)
  135. end
  136. iterpos = k
  137. return k,v,t[iternum].set
  138. end
  139. end
  140. do
  141. -- Handle the initial scan of LoD data modules, storing in this local table so the sets metatable can find em
  142. local lodmodules = {}
  143. for i = 1, GetNumAddOns() do
  144. local metadata = GetAddOnMetadata(i, "X-PeriodicTable-3.1-Module")
  145. if metadata then
  146. local name, _, _, enabled = GetAddOnInfo(i)
  147. if enabled then
  148. lodmodules[metadata] = name
  149. end
  150. end
  151. end
  152. PT3.sets = setmetatable(PT3.sets or {}, {
  153. __index = function(self, key)
  154. local base = key:match("^([^%.]+)%.") or key
  155. if lodmodules[base] then
  156. LoadAddOn(lodmodules[base])
  157. lodmodules[base] = nil -- don't try to load again
  158. -- still may need to generate multiset or something like that, so re-call the metamethod if need be
  159. return self[key]
  160. end
  161. makeNonPresentMultiSet(key) -- this will store it as empty if this is an invalid set.
  162. return self[key]
  163. end
  164. })
  165. end
  166. PT3.embedversions = PT3.embedversions or {}
  167. sets = PT3.sets
  168. embedversions = PT3.embedversions
  169. cache = setmetatable({}, {
  170. __mode = 'v', -- weaken this table's values.
  171. __index = function(self, key)
  172. -- Get the setstring in question. This call does most of the hairy stuff
  173. -- like putting together implied but absent multisets and finding child sets
  174. local setstring = sets[key]
  175. if not setstring then
  176. return
  177. end
  178. if setstring:sub(1,2) == "m," then
  179. -- This table is a list of references to the members of this set.
  180. self[key] = {}
  181. local working = self[key]
  182. for childset in setstring:sub(3):gmatch("([^,]+)") do
  183. if childset ~= key then -- infinite loops is bad
  184. local pointer = cache[childset]
  185. if pointer then
  186. local _, firstv = next(pointer)
  187. if type(firstv) == "table" then
  188. -- This is a multiset, copy its references
  189. for _,v in ipairs(pointer) do
  190. working[#working+1] = v
  191. end
  192. elseif firstv then
  193. -- This is not a multiset, just stick a reference in.
  194. working[#working+1] = pointer
  195. end
  196. end
  197. end
  198. end
  199. return working
  200. else
  201. -- normal ol' set. Well, maybe not, but close enough.
  202. self[key] = {}
  203. local working = self[key]
  204. for itemstring in setstring:gmatch("([^,]+)") do
  205. -- for each item (comma seperated)..
  206. -- ...check to see if we have a value set (ie "14543:1121")
  207. local id, value = itemstring:match("^([^:]+):(.+)$")
  208. -- if we don't, (ie "14421,12312"), then set the value to true.
  209. id, value = tonumber(id) or tonumber(itemstring), value or true
  210. assert(id, 'malformed set? '..key)
  211. working[id] = value
  212. end
  213. -- stick the set name in there so that we can find out which set an item originally came from.
  214. working.set = key
  215. return working
  216. end
  217. end
  218. })
  219. ---------------------------------------------
  220. -- API --
  221. ---------------------------------------------
  222. -- These three are pretty simple. Note that non-present chunks will be generated by the metamethods.
  223. function PT3:GetSetTable(set)
  224. assert(type(set) == "string", "Invalid arg1: set must be a string")
  225. return cache[set]
  226. end
  227. function PT3:GetSetString(set)
  228. assert(type(set) == "string", "Invalid arg1: set must be a string")
  229. return sets[set]
  230. end
  231. function PT3:IsSetMulti(set)
  232. assert(type(set) == "string", "Invalid arg1: set must be a string")
  233. -- Check if this set's a multiset by checking if its table contains tables instead of strings/booleans
  234. local pointer = cache[set]
  235. if not pointer then
  236. return
  237. end
  238. local _, firstv = next(pointer)
  239. if type(firstv) == "table" then
  240. return true
  241. else
  242. return false
  243. end
  244. end
  245. function PT3:IterateSet(set)
  246. -- most of the work here is handled by the local functions above.
  247. --!! this could maybe use some improvement...
  248. local t = cache[set]
  249. assert(t, "Invalid set: "..set)
  250. if self:IsSetMulti(set) then
  251. iternum, iterpos = 1, nil
  252. return multisetiter, t
  253. else
  254. iterpos = nil
  255. return setiter, t
  256. end
  257. end
  258. -- Check if the item's contained in this set or any of it's child sets. If it is, return the value
  259. -- (which is true for items with no value set) and the set where the item is contained in data.
  260. function PT3:ItemInSet(item, set)
  261. assert(type(item) == "number" or type(item) == "string", "Invalid arg1: item must be a number or item link")
  262. assert(type(set) == "string", "Invalid arg2: set must be a string")
  263. -- Type the passed item out to an itemid.
  264. item = getItemID(item)
  265. assert(item ~= 0,"Invalid arg1: invalid item.")
  266. local pointer = cache[set]
  267. if not pointer then
  268. return
  269. end
  270. local _, firstv = next(pointer)
  271. if type(firstv) == "table" then
  272. -- The requested set is a multiset, iterate its children. Return the first matching item.
  273. for _,v in ipairs(pointer) do
  274. if v[item] then
  275. return v[item], v.set
  276. end
  277. end
  278. elseif pointer[item] then
  279. -- Not a multiset, just return the value and set name.
  280. return pointer[item], pointer.set
  281. end
  282. end
  283. function PT3:AddData(arg1, arg2, arg3)
  284. assert(type(arg1) == "string", "Invalid arg1: name must be a string")
  285. assert(type(arg2) == "string" or type(arg2) == "table", "Invalid arg2: must be set contents string or table, or revision string")
  286. assert((arg3 and type(arg3) == "table") or not arg3, "Invalid arg3: must be a table")
  287. if not arg3 and type(arg2) == "string" then
  288. -- Just a string.
  289. local replacing
  290. if rawget(sets, arg1) then
  291. replacing = true
  292. end
  293. sets[arg1] = arg2
  294. -- Clear the cache of this set's data if it exists, avoiding invoking the metamethod.
  295. -- No sense generating data if we're just gonna nuke it anyway ;)
  296. if replacing then
  297. shredCache(arg1)
  298. end
  299. else
  300. -- Table of sets passed.
  301. if arg3 then
  302. -- Woot, version numbers and everything.
  303. assert(type(arg2) == "string", "Invalid arg2: must be revision string")
  304. local version = tonumber(arg2:match("(%d+)"))
  305. if embedversions[arg1] and embedversions[arg1] >= version then
  306. -- The loaded version is newer than this one.
  307. return
  308. end
  309. embedversions[arg1] = version
  310. for k,v in pairs(arg3) do
  311. -- Looks good, throw 'em in there one by one
  312. self:AddData(k,v)
  313. end
  314. else
  315. -- Boo, no version numbers. Just overwrite all these sets.
  316. for k,v in pairs(arg2) do
  317. self:AddData(k,v)
  318. end
  319. end
  320. end
  321. end
  322. function PT3:ItemSearch(item)
  323. assert(type(item) == "number" or type(item) == "string", "Invalid arg1: item must be a number or item link")
  324. item = tonumber(item) or tonumber(item:match("item:(%d+)"))
  325. if item == 0 then
  326. self:error("Invalid arg1: invalid item.")
  327. end
  328. local matches = {}
  329. for k,v in pairs(self.sets) do
  330. local _, set = self:ItemInSet(item, k)
  331. if set then
  332. local have
  333. for _,v in ipairs(matches) do
  334. if v == set then
  335. have = true
  336. end
  337. end
  338. if not have then
  339. table.insert(matches, set)
  340. end
  341. end
  342. end
  343. if #matches > 0 then
  344. return matches
  345. end
  346. end
  347. -- ie, LibStub('PeriodicTable-3.1')('InstanceLoot') == LibStub('LibPeriodicTable-3.1'):GetSetTable('InstanceLoot')
  348. setmetatable(PT3, { __call = PT3.GetSetTable })