clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ |
||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
||
4 | * |
||
5 | * Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions are met: |
||
7 | * * Redistributions of source code must retain the above copyright |
||
8 | * notice, this list of conditions and the following disclaimer. |
||
9 | * * Redistributions in binary form must reproduce the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer in the |
||
11 | * documentation and/or other materials provided with the distribution. |
||
12 | * * Neither the name of the OpenSimulator Project nor the |
||
13 | * names of its contributors may be used to endorse or promote products |
||
14 | * derived from this software without specific prior written permission. |
||
15 | * |
||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY |
||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
26 | */ |
||
27 | |||
28 | using System; |
||
29 | using System.Collections.Generic; |
||
30 | using System.Data; |
||
31 | using System.Reflection; |
||
32 | using log4net; |
||
33 | #if CSharpSqlite |
||
34 | using Community.CsharpSqlite.Sqlite; |
||
35 | #else |
||
36 | using Mono.Data.Sqlite; |
||
37 | #endif |
||
38 | using OpenMetaverse; |
||
39 | using OpenSim.Framework; |
||
40 | |||
41 | namespace OpenSim.Data.SQLite |
||
42 | { |
||
43 | /// <summary> |
||
44 | /// An Inventory Interface to the SQLite database |
||
45 | /// </summary> |
||
46 | public class SQLiteInventoryStore : SQLiteUtil, IInventoryDataPlugin |
||
47 | { |
||
48 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
49 | |||
50 | private const string invItemsSelect = "select * from inventoryitems"; |
||
51 | private const string invFoldersSelect = "select * from inventoryfolders"; |
||
52 | |||
53 | private static SqliteConnection conn; |
||
54 | private static DataSet ds; |
||
55 | private static SqliteDataAdapter invItemsDa; |
||
56 | private static SqliteDataAdapter invFoldersDa; |
||
57 | |||
58 | private static bool m_Initialized = false; |
||
59 | |||
60 | public void Initialise() |
||
61 | { |
||
62 | m_log.Info("[SQLiteInventoryData]: " + Name + " cannot be default-initialized!"); |
||
63 | throw new PluginNotInitialisedException(Name); |
||
64 | } |
||
65 | |||
66 | /// <summary> |
||
67 | /// <list type="bullet"> |
||
68 | /// <item>Initialises Inventory interface</item> |
||
69 | /// <item>Loads and initialises a new SQLite connection and maintains it.</item> |
||
70 | /// <item>use default URI if connect string string is empty.</item> |
||
71 | /// </list> |
||
72 | /// </summary> |
||
73 | /// <param name="dbconnect">connect string</param> |
||
74 | public void Initialise(string dbconnect) |
||
75 | { |
||
76 | if (!m_Initialized) |
||
77 | { |
||
78 | m_Initialized = true; |
||
79 | |||
80 | if (Util.IsWindows()) |
||
81 | Util.LoadArchSpecificWindowsDll("sqlite3.dll"); |
||
82 | |||
83 | if (dbconnect == string.Empty) |
||
84 | { |
||
85 | dbconnect = "URI=file:inventoryStore.db,version=3"; |
||
86 | } |
||
87 | m_log.Info("[INVENTORY DB]: Sqlite - connecting: " + dbconnect); |
||
88 | conn = new SqliteConnection(dbconnect); |
||
89 | |||
90 | conn.Open(); |
||
91 | |||
92 | Assembly assem = GetType().Assembly; |
||
93 | Migration m = new Migration(conn, assem, "InventoryStore"); |
||
94 | m.Update(); |
||
95 | |||
96 | SqliteCommand itemsSelectCmd = new SqliteCommand(invItemsSelect, conn); |
||
97 | invItemsDa = new SqliteDataAdapter(itemsSelectCmd); |
||
98 | // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); |
||
99 | |||
100 | SqliteCommand foldersSelectCmd = new SqliteCommand(invFoldersSelect, conn); |
||
101 | invFoldersDa = new SqliteDataAdapter(foldersSelectCmd); |
||
102 | |||
103 | ds = new DataSet(); |
||
104 | |||
105 | ds.Tables.Add(createInventoryFoldersTable()); |
||
106 | invFoldersDa.Fill(ds.Tables["inventoryfolders"]); |
||
107 | setupFoldersCommands(invFoldersDa, conn); |
||
108 | CreateDataSetMapping(invFoldersDa, "inventoryfolders"); |
||
109 | m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); |
||
110 | |||
111 | ds.Tables.Add(createInventoryItemsTable()); |
||
112 | invItemsDa.Fill(ds.Tables["inventoryitems"]); |
||
113 | setupItemsCommands(invItemsDa, conn); |
||
114 | CreateDataSetMapping(invItemsDa, "inventoryitems"); |
||
115 | m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); |
||
116 | |||
117 | ds.AcceptChanges(); |
||
118 | } |
||
119 | } |
||
120 | |||
121 | /// <summary> |
||
122 | /// Closes the inventory interface |
||
123 | /// </summary> |
||
124 | public void Dispose() |
||
125 | { |
||
126 | if (conn != null) |
||
127 | { |
||
128 | conn.Close(); |
||
129 | conn = null; |
||
130 | } |
||
131 | if (invItemsDa != null) |
||
132 | { |
||
133 | invItemsDa.Dispose(); |
||
134 | invItemsDa = null; |
||
135 | } |
||
136 | if (invFoldersDa != null) |
||
137 | { |
||
138 | invFoldersDa.Dispose(); |
||
139 | invFoldersDa = null; |
||
140 | } |
||
141 | if (ds != null) |
||
142 | { |
||
143 | ds.Dispose(); |
||
144 | ds = null; |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /// <summary> |
||
149 | /// |
||
150 | /// </summary> |
||
151 | /// <param name="row"></param> |
||
152 | /// <returns></returns> |
||
153 | public InventoryItemBase buildItem(DataRow row) |
||
154 | { |
||
155 | InventoryItemBase item = new InventoryItemBase(); |
||
156 | item.ID = new UUID((string) row["UUID"]); |
||
157 | item.AssetID = new UUID((string) row["assetID"]); |
||
158 | item.AssetType = Convert.ToInt32(row["assetType"]); |
||
159 | item.InvType = Convert.ToInt32(row["invType"]); |
||
160 | item.Folder = new UUID((string) row["parentFolderID"]); |
||
161 | item.Owner = new UUID((string) row["avatarID"]); |
||
162 | item.CreatorIdentification = (string)row["creatorsID"]; |
||
163 | item.Name = (string) row["inventoryName"]; |
||
164 | item.Description = (string) row["inventoryDescription"]; |
||
165 | |||
166 | item.NextPermissions = Convert.ToUInt32(row["inventoryNextPermissions"]); |
||
167 | item.CurrentPermissions = Convert.ToUInt32(row["inventoryCurrentPermissions"]); |
||
168 | item.BasePermissions = Convert.ToUInt32(row["inventoryBasePermissions"]); |
||
169 | item.EveryOnePermissions = Convert.ToUInt32(row["inventoryEveryOnePermissions"]); |
||
170 | item.GroupPermissions = Convert.ToUInt32(row["inventoryGroupPermissions"]); |
||
171 | |||
172 | // new fields |
||
173 | if (!Convert.IsDBNull(row["salePrice"])) |
||
174 | item.SalePrice = Convert.ToInt32(row["salePrice"]); |
||
175 | |||
176 | if (!Convert.IsDBNull(row["saleType"])) |
||
177 | item.SaleType = Convert.ToByte(row["saleType"]); |
||
178 | |||
179 | if (!Convert.IsDBNull(row["creationDate"])) |
||
180 | item.CreationDate = Convert.ToInt32(row["creationDate"]); |
||
181 | |||
182 | if (!Convert.IsDBNull(row["groupID"])) |
||
183 | item.GroupID = new UUID((string)row["groupID"]); |
||
184 | |||
185 | if (!Convert.IsDBNull(row["groupOwned"])) |
||
186 | item.GroupOwned = Convert.ToBoolean(row["groupOwned"]); |
||
187 | |||
188 | if (!Convert.IsDBNull(row["Flags"])) |
||
189 | item.Flags = Convert.ToUInt32(row["Flags"]); |
||
190 | |||
191 | return item; |
||
192 | } |
||
193 | |||
194 | /// <summary> |
||
195 | /// Fill a database row with item data |
||
196 | /// </summary> |
||
197 | /// <param name="row"></param> |
||
198 | /// <param name="item"></param> |
||
199 | private static void fillItemRow(DataRow row, InventoryItemBase item) |
||
200 | { |
||
201 | row["UUID"] = item.ID.ToString(); |
||
202 | row["assetID"] = item.AssetID.ToString(); |
||
203 | row["assetType"] = item.AssetType; |
||
204 | row["invType"] = item.InvType; |
||
205 | row["parentFolderID"] = item.Folder.ToString(); |
||
206 | row["avatarID"] = item.Owner.ToString(); |
||
207 | row["creatorsID"] = item.CreatorIdentification.ToString(); |
||
208 | row["inventoryName"] = item.Name; |
||
209 | row["inventoryDescription"] = item.Description; |
||
210 | |||
211 | row["inventoryNextPermissions"] = item.NextPermissions; |
||
212 | row["inventoryCurrentPermissions"] = item.CurrentPermissions; |
||
213 | row["inventoryBasePermissions"] = item.BasePermissions; |
||
214 | row["inventoryEveryOnePermissions"] = item.EveryOnePermissions; |
||
215 | row["inventoryGroupPermissions"] = item.GroupPermissions; |
||
216 | |||
217 | // new fields |
||
218 | row["salePrice"] = item.SalePrice; |
||
219 | row["saleType"] = item.SaleType; |
||
220 | row["creationDate"] = item.CreationDate; |
||
221 | row["groupID"] = item.GroupID.ToString(); |
||
222 | row["groupOwned"] = item.GroupOwned; |
||
223 | row["flags"] = item.Flags; |
||
224 | } |
||
225 | |||
226 | /// <summary> |
||
227 | /// Add inventory folder |
||
228 | /// </summary> |
||
229 | /// <param name="folder">Folder base</param> |
||
230 | /// <param name="add">true=create folder. false=update existing folder</param> |
||
231 | /// <remarks>nasty</remarks> |
||
232 | private void addFolder(InventoryFolderBase folder, bool add) |
||
233 | { |
||
234 | lock (ds) |
||
235 | { |
||
236 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
237 | |||
238 | DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); |
||
239 | if (inventoryRow == null) |
||
240 | { |
||
241 | if (! add) |
||
242 | m_log.ErrorFormat("Interface Misuse: Attempting to Update non-existant inventory folder: {0}", folder.ID); |
||
243 | |||
244 | inventoryRow = inventoryFolderTable.NewRow(); |
||
245 | fillFolderRow(inventoryRow, folder); |
||
246 | inventoryFolderTable.Rows.Add(inventoryRow); |
||
247 | } |
||
248 | else |
||
249 | { |
||
250 | if (add) |
||
251 | m_log.ErrorFormat("Interface Misuse: Attempting to Add inventory folder that already exists: {0}", folder.ID); |
||
252 | |||
253 | fillFolderRow(inventoryRow, folder); |
||
254 | } |
||
255 | |||
256 | invFoldersDa.Update(ds, "inventoryfolders"); |
||
257 | } |
||
258 | } |
||
259 | |||
260 | /// <summary> |
||
261 | /// Move an inventory folder |
||
262 | /// </summary> |
||
263 | /// <param name="folder">folder base</param> |
||
264 | private void moveFolder(InventoryFolderBase folder) |
||
265 | { |
||
266 | lock (ds) |
||
267 | { |
||
268 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
269 | |||
270 | DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); |
||
271 | if (inventoryRow == null) |
||
272 | { |
||
273 | inventoryRow = inventoryFolderTable.NewRow(); |
||
274 | fillFolderRow(inventoryRow, folder); |
||
275 | inventoryFolderTable.Rows.Add(inventoryRow); |
||
276 | } |
||
277 | else |
||
278 | { |
||
279 | moveFolderRow(inventoryRow, folder); |
||
280 | } |
||
281 | |||
282 | invFoldersDa.Update(ds, "inventoryfolders"); |
||
283 | } |
||
284 | } |
||
285 | |||
286 | /// <summary> |
||
287 | /// add an item in inventory |
||
288 | /// </summary> |
||
289 | /// <param name="item">the item</param> |
||
290 | /// <param name="add">true=add item ; false=update existing item</param> |
||
291 | private void addItem(InventoryItemBase item, bool add) |
||
292 | { |
||
293 | lock (ds) |
||
294 | { |
||
295 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; |
||
296 | |||
297 | DataRow inventoryRow = inventoryItemTable.Rows.Find(item.ID.ToString()); |
||
298 | if (inventoryRow == null) |
||
299 | { |
||
300 | if (!add) |
||
301 | m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Update non-existant inventory item: {0}", item.ID); |
||
302 | |||
303 | inventoryRow = inventoryItemTable.NewRow(); |
||
304 | fillItemRow(inventoryRow, item); |
||
305 | inventoryItemTable.Rows.Add(inventoryRow); |
||
306 | } |
||
307 | else |
||
308 | { |
||
309 | if (add) |
||
310 | m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Add inventory item that already exists: {0}", item.ID); |
||
311 | |||
312 | fillItemRow(inventoryRow, item); |
||
313 | } |
||
314 | |||
315 | invItemsDa.Update(ds, "inventoryitems"); |
||
316 | |||
317 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
318 | |||
319 | inventoryRow = inventoryFolderTable.Rows.Find(item.Folder.ToString()); |
||
320 | if (inventoryRow != null) //MySQL doesn't throw an exception here, so sqlite shouldn't either. |
||
321 | inventoryRow["version"] = (int)inventoryRow["version"] + 1; |
||
322 | |||
323 | invFoldersDa.Update(ds, "inventoryfolders"); |
||
324 | } |
||
325 | } |
||
326 | |||
327 | /// <summary> |
||
328 | /// TODO : DataSet commit |
||
329 | /// </summary> |
||
330 | public void Shutdown() |
||
331 | { |
||
332 | // TODO: DataSet commit |
||
333 | } |
||
334 | |||
335 | /// <summary> |
||
336 | /// The name of this DB provider |
||
337 | /// </summary> |
||
338 | /// <returns>Name of DB provider</returns> |
||
339 | public string Name |
||
340 | { |
||
341 | get { return "SQLite Inventory Data Interface"; } |
||
342 | } |
||
343 | |||
344 | /// <summary> |
||
345 | /// Returns the version of this DB provider |
||
346 | /// </summary> |
||
347 | /// <returns>A string containing the DB provider version</returns> |
||
348 | public string Version |
||
349 | { |
||
350 | get |
||
351 | { |
||
352 | Module module = GetType().Module; |
||
353 | // string dllName = module.Assembly.ManifestModule.Name; |
||
354 | Version dllVersion = module.Assembly.GetName().Version; |
||
355 | |||
356 | |||
357 | return |
||
358 | string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, |
||
359 | dllVersion.Revision); |
||
360 | } |
||
361 | } |
||
362 | |||
363 | /// <summary> |
||
364 | /// Returns a list of inventory items contained within the specified folder |
||
365 | /// </summary> |
||
366 | /// <param name="folderID">The UUID of the target folder</param> |
||
367 | /// <returns>A List of InventoryItemBase items</returns> |
||
368 | public List<InventoryItemBase> getInventoryInFolder(UUID folderID) |
||
369 | { |
||
370 | lock (ds) |
||
371 | { |
||
372 | List<InventoryItemBase> retval = new List<InventoryItemBase>(); |
||
373 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; |
||
374 | string selectExp = "parentFolderID = '" + folderID + "'"; |
||
375 | DataRow[] rows = inventoryItemTable.Select(selectExp); |
||
376 | foreach (DataRow row in rows) |
||
377 | { |
||
378 | retval.Add(buildItem(row)); |
||
379 | } |
||
380 | |||
381 | return retval; |
||
382 | } |
||
383 | } |
||
384 | |||
385 | /// <summary> |
||
386 | /// Returns a list of the root folders within a users inventory |
||
387 | /// </summary> |
||
388 | /// <param name="user">The user whos inventory is to be searched</param> |
||
389 | /// <returns>A list of folder objects</returns> |
||
390 | public List<InventoryFolderBase> getUserRootFolders(UUID user) |
||
391 | { |
||
392 | return new List<InventoryFolderBase>(); |
||
393 | } |
||
394 | |||
395 | // see InventoryItemBase.getUserRootFolder |
||
396 | public InventoryFolderBase getUserRootFolder(UUID user) |
||
397 | { |
||
398 | lock (ds) |
||
399 | { |
||
400 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); |
||
401 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
402 | string selectExp = "agentID = '" + user + "' AND parentID = '" + UUID.Zero + "'"; |
||
403 | DataRow[] rows = inventoryFolderTable.Select(selectExp); |
||
404 | foreach (DataRow row in rows) |
||
405 | { |
||
406 | folders.Add(buildFolder(row)); |
||
407 | } |
||
408 | |||
409 | // There should only ever be one root folder for a user. However, if there's more |
||
410 | // than one we'll simply use the first one rather than failing. It would be even |
||
411 | // nicer to print some message to this effect, but this feels like it's too low a |
||
412 | // to put such a message out, and it's too minor right now to spare the time to |
||
413 | // suitably refactor. |
||
414 | if (folders.Count > 0) |
||
415 | { |
||
416 | return folders[0]; |
||
417 | } |
||
418 | |||
419 | return null; |
||
420 | } |
||
421 | } |
||
422 | |||
423 | /// <summary> |
||
424 | /// Append a list of all the child folders of a parent folder |
||
425 | /// </summary> |
||
426 | /// <param name="folders">list where folders will be appended</param> |
||
427 | /// <param name="parentID">ID of parent</param> |
||
428 | protected void getInventoryFolders(ref List<InventoryFolderBase> folders, UUID parentID) |
||
429 | { |
||
430 | lock (ds) |
||
431 | { |
||
432 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
433 | string selectExp = "parentID = '" + parentID + "'"; |
||
434 | DataRow[] rows = inventoryFolderTable.Select(selectExp); |
||
435 | foreach (DataRow row in rows) |
||
436 | { |
||
437 | folders.Add(buildFolder(row)); |
||
438 | } |
||
439 | |||
440 | } |
||
441 | } |
||
442 | |||
443 | /// <summary> |
||
444 | /// Returns a list of inventory folders contained in the folder 'parentID' |
||
445 | /// </summary> |
||
446 | /// <param name="parentID">The folder to get subfolders for</param> |
||
447 | /// <returns>A list of inventory folders</returns> |
||
448 | public List<InventoryFolderBase> getInventoryFolders(UUID parentID) |
||
449 | { |
||
450 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); |
||
451 | getInventoryFolders(ref folders, parentID); |
||
452 | return folders; |
||
453 | } |
||
454 | |||
455 | /// <summary> |
||
456 | /// See IInventoryDataPlugin |
||
457 | /// </summary> |
||
458 | /// <param name="parentID"></param> |
||
459 | /// <returns></returns> |
||
460 | public List<InventoryFolderBase> getFolderHierarchy(UUID parentID) |
||
461 | { |
||
462 | /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one |
||
463 | * - We will only need to hit the database twice instead of n times. |
||
464 | * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned |
||
465 | * by the same person, each user only has 1 inventory heirarchy |
||
466 | * - The returned list is not ordered, instead of breadth-first ordered |
||
467 | There are basically 2 usage cases for getFolderHeirarchy: |
||
468 | 1) Getting the user's entire inventory heirarchy when they log in |
||
469 | 2) Finding a subfolder heirarchy to delete when emptying the trash. |
||
470 | This implementation will pull all inventory folders from the database, and then prune away any folder that |
||
471 | is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the |
||
472 | database than to make n requests. This pays off only if requested heirarchy is large. |
||
473 | By making this choice, we are making the worst case better at the cost of making the best case worse |
||
474 | - Francis |
||
475 | */ |
||
476 | |||
477 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); |
||
478 | DataRow[] folderRows = null, parentRow; |
||
479 | InventoryFolderBase parentFolder = null; |
||
480 | lock (ds) |
||
481 | { |
||
482 | /* Fetch the parent folder from the database to determine the agent ID. |
||
483 | * Then fetch all inventory folders for that agent from the agent ID. |
||
484 | */ |
||
485 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
486 | string selectExp = "UUID = '" + parentID + "'"; |
||
487 | parentRow = inventoryFolderTable.Select(selectExp); // Assume at most 1 result |
||
488 | if (parentRow.GetLength(0) >= 1) // No result means parent folder does not exist |
||
489 | { |
||
490 | parentFolder = buildFolder(parentRow[0]); |
||
491 | UUID agentID = parentFolder.Owner; |
||
492 | selectExp = "agentID = '" + agentID + "'"; |
||
493 | folderRows = inventoryFolderTable.Select(selectExp); |
||
494 | } |
||
495 | |||
496 | if (folderRows != null && folderRows.GetLength(0) >= 1) // No result means parent folder does not exist |
||
497 | { // or has no children |
||
498 | /* if we're querying the root folder, just return an unordered list of all folders in the user's |
||
499 | * inventory |
||
500 | */ |
||
501 | if (parentFolder.ParentID == UUID.Zero) |
||
502 | { |
||
503 | foreach (DataRow row in folderRows) |
||
504 | { |
||
505 | InventoryFolderBase curFolder = buildFolder(row); |
||
506 | if (curFolder.ID != parentID) // Return all folders except the parent folder of heirarchy |
||
507 | folders.Add(buildFolder(row)); |
||
508 | } |
||
509 | } // If requesting root folder |
||
510 | /* else we are querying a non-root folder. We currently have a list of all of the user's folders, |
||
511 | * we must construct a list of all folders in the heirarchy below parentID. |
||
512 | * Our first step will be to construct a hash table of all folders, indexed by parent ID. |
||
513 | * Once we have constructed the hash table, we will do a breadth-first traversal on the tree using the |
||
514 | * hash table to find child folders. |
||
515 | */ |
||
516 | else |
||
517 | { // Querying a non-root folder |
||
518 | |||
519 | // Build a hash table of all user's inventory folders, indexed by each folder's parent ID |
||
520 | Dictionary<UUID, List<InventoryFolderBase>> hashtable = |
||
521 | new Dictionary<UUID, List<InventoryFolderBase>>(folderRows.GetLength(0)); |
||
522 | |||
523 | foreach (DataRow row in folderRows) |
||
524 | { |
||
525 | InventoryFolderBase curFolder = buildFolder(row); |
||
526 | if (curFolder.ParentID != UUID.Zero) // Discard root of tree - not needed |
||
527 | { |
||
528 | if (hashtable.ContainsKey(curFolder.ParentID)) |
||
529 | { |
||
530 | // Current folder already has a sibling - append to sibling list |
||
531 | hashtable[curFolder.ParentID].Add(curFolder); |
||
532 | } |
||
533 | else |
||
534 | { |
||
535 | List<InventoryFolderBase> siblingList = new List<InventoryFolderBase>(); |
||
536 | siblingList.Add(curFolder); |
||
537 | // Current folder has no known (yet) siblings |
||
538 | hashtable.Add(curFolder.ParentID, siblingList); |
||
539 | } |
||
540 | } |
||
541 | } // For all inventory folders |
||
542 | |||
543 | // Note: Could release the ds lock here - we don't access folderRows or the database anymore. |
||
544 | // This is somewhat of a moot point as the callers of this function usually lock db anyways. |
||
545 | |||
546 | if (hashtable.ContainsKey(parentID)) // if requested folder does have children |
||
547 | folders.AddRange(hashtable[parentID]); |
||
548 | |||
549 | // BreadthFirstSearch build inventory tree **Note: folders.Count is *not* static |
||
550 | for (int i = 0; i < folders.Count; i++) |
||
551 | if (hashtable.ContainsKey(folders[i].ID)) |
||
552 | folders.AddRange(hashtable[folders[i].ID]); |
||
553 | |||
554 | } // if requesting a subfolder heirarchy |
||
555 | } // if folder parentID exists and has children |
||
556 | } // lock ds |
||
557 | return folders; |
||
558 | } |
||
559 | |||
560 | /// <summary> |
||
561 | /// Returns an inventory item by its UUID |
||
562 | /// </summary> |
||
563 | /// <param name="item">The UUID of the item to be returned</param> |
||
564 | /// <returns>A class containing item information</returns> |
||
565 | public InventoryItemBase getInventoryItem(UUID item) |
||
566 | { |
||
567 | lock (ds) |
||
568 | { |
||
569 | DataRow row = ds.Tables["inventoryitems"].Rows.Find(item.ToString()); |
||
570 | if (row != null) |
||
571 | { |
||
572 | return buildItem(row); |
||
573 | } |
||
574 | else |
||
575 | { |
||
576 | return null; |
||
577 | } |
||
578 | } |
||
579 | } |
||
580 | |||
581 | /// <summary> |
||
582 | /// Returns a specified inventory folder by its UUID |
||
583 | /// </summary> |
||
584 | /// <param name="folder">The UUID of the folder to be returned</param> |
||
585 | /// <returns>A class containing folder information</returns> |
||
586 | public InventoryFolderBase getInventoryFolder(UUID folder) |
||
587 | { |
||
588 | // TODO: Deep voodoo here. If you enable this code then |
||
589 | // multi region breaks. No idea why, but I figured it was |
||
590 | // better to leave multi region at this point. It does mean |
||
591 | // that you don't get to see system textures why creating |
||
592 | // clothes and the like. :( |
||
593 | lock (ds) |
||
594 | { |
||
595 | DataRow row = ds.Tables["inventoryfolders"].Rows.Find(folder.ToString()); |
||
596 | if (row != null) |
||
597 | { |
||
598 | return buildFolder(row); |
||
599 | } |
||
600 | else |
||
601 | { |
||
602 | return null; |
||
603 | } |
||
604 | } |
||
605 | } |
||
606 | |||
607 | /// <summary> |
||
608 | /// Creates a new inventory item based on item |
||
609 | /// </summary> |
||
610 | /// <param name="item">The item to be created</param> |
||
611 | public void addInventoryItem(InventoryItemBase item) |
||
612 | { |
||
613 | addItem(item, true); |
||
614 | } |
||
615 | |||
616 | /// <summary> |
||
617 | /// Updates an inventory item with item (updates based on ID) |
||
618 | /// </summary> |
||
619 | /// <param name="item">The updated item</param> |
||
620 | public void updateInventoryItem(InventoryItemBase item) |
||
621 | { |
||
622 | addItem(item, false); |
||
623 | } |
||
624 | |||
625 | /// <summary> |
||
626 | /// Delete an inventory item |
||
627 | /// </summary> |
||
628 | /// <param name="item">The item UUID</param> |
||
629 | public void deleteInventoryItem(UUID itemID) |
||
630 | { |
||
631 | lock (ds) |
||
632 | { |
||
633 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; |
||
634 | |||
635 | DataRow inventoryRow = inventoryItemTable.Rows.Find(itemID.ToString()); |
||
636 | if (inventoryRow != null) |
||
637 | { |
||
638 | inventoryRow.Delete(); |
||
639 | } |
||
640 | |||
641 | invItemsDa.Update(ds, "inventoryitems"); |
||
642 | } |
||
643 | } |
||
644 | |||
645 | public InventoryItemBase queryInventoryItem(UUID itemID) |
||
646 | { |
||
647 | return getInventoryItem(itemID); |
||
648 | } |
||
649 | |||
650 | public InventoryFolderBase queryInventoryFolder(UUID folderID) |
||
651 | { |
||
652 | return getInventoryFolder(folderID); |
||
653 | } |
||
654 | |||
655 | /// <summary> |
||
656 | /// Delete all items in the specified folder |
||
657 | /// </summary> |
||
658 | /// <param name="folderId">id of the folder, whose item content should be deleted</param> |
||
659 | /// <todo>this is horribly inefficient, but I don't want to ruin the overall structure of this implementation</todo> |
||
660 | private void deleteItemsInFolder(UUID folderId) |
||
661 | { |
||
662 | List<InventoryItemBase> items = getInventoryInFolder(folderId); |
||
663 | |||
664 | foreach (InventoryItemBase i in items) |
||
665 | deleteInventoryItem(i.ID); |
||
666 | } |
||
667 | |||
668 | /// <summary> |
||
669 | /// Adds a new folder specified by folder |
||
670 | /// </summary> |
||
671 | /// <param name="folder">The inventory folder</param> |
||
672 | public void addInventoryFolder(InventoryFolderBase folder) |
||
673 | { |
||
674 | addFolder(folder, true); |
||
675 | } |
||
676 | |||
677 | /// <summary> |
||
678 | /// Updates a folder based on its ID with folder |
||
679 | /// </summary> |
||
680 | /// <param name="folder">The inventory folder</param> |
||
681 | public void updateInventoryFolder(InventoryFolderBase folder) |
||
682 | { |
||
683 | addFolder(folder, false); |
||
684 | } |
||
685 | |||
686 | /// <summary> |
||
687 | /// Moves a folder based on its ID with folder |
||
688 | /// </summary> |
||
689 | /// <param name="folder">The inventory folder</param> |
||
690 | public void moveInventoryFolder(InventoryFolderBase folder) |
||
691 | { |
||
692 | moveFolder(folder); |
||
693 | } |
||
694 | |||
695 | /// <summary> |
||
696 | /// Delete a folder |
||
697 | /// </summary> |
||
698 | /// <remarks> |
||
699 | /// This will clean-up any child folders and child items as well |
||
700 | /// </remarks> |
||
701 | /// <param name="folderID">the folder UUID</param> |
||
702 | public void deleteInventoryFolder(UUID folderID) |
||
703 | { |
||
704 | lock (ds) |
||
705 | { |
||
706 | List<InventoryFolderBase> subFolders = getFolderHierarchy(folderID); |
||
707 | |||
708 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; |
||
709 | DataRow inventoryRow; |
||
710 | |||
711 | //Delete all sub-folders |
||
712 | foreach (InventoryFolderBase f in subFolders) |
||
713 | { |
||
714 | inventoryRow = inventoryFolderTable.Rows.Find(f.ID.ToString()); |
||
715 | if (inventoryRow != null) |
||
716 | { |
||
717 | deleteItemsInFolder(f.ID); |
||
718 | inventoryRow.Delete(); |
||
719 | } |
||
720 | } |
||
721 | |||
722 | //Delete the actual row |
||
723 | inventoryRow = inventoryFolderTable.Rows.Find(folderID.ToString()); |
||
724 | if (inventoryRow != null) |
||
725 | { |
||
726 | deleteItemsInFolder(folderID); |
||
727 | inventoryRow.Delete(); |
||
728 | } |
||
729 | |||
730 | invFoldersDa.Update(ds, "inventoryfolders"); |
||
731 | } |
||
732 | } |
||
733 | |||
734 | /*********************************************************************** |
||
735 | * |
||
736 | * Data Table definitions |
||
737 | * |
||
738 | **********************************************************************/ |
||
739 | |||
740 | protected void CreateDataSetMapping(IDataAdapter da, string tableName) |
||
741 | { |
||
742 | ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); |
||
743 | foreach (DataColumn col in ds.Tables[tableName].Columns) |
||
744 | { |
||
745 | dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); |
||
746 | } |
||
747 | } |
||
748 | |||
749 | /// <summary> |
||
750 | /// Create the "inventoryitems" table |
||
751 | /// </summary> |
||
752 | private static DataTable createInventoryItemsTable() |
||
753 | { |
||
754 | DataTable inv = new DataTable("inventoryitems"); |
||
755 | |||
756 | createCol(inv, "UUID", typeof (String)); //inventoryID |
||
757 | createCol(inv, "assetID", typeof (String)); |
||
758 | createCol(inv, "assetType", typeof (Int32)); |
||
759 | createCol(inv, "invType", typeof (Int32)); |
||
760 | createCol(inv, "parentFolderID", typeof (String)); |
||
761 | createCol(inv, "avatarID", typeof (String)); |
||
762 | createCol(inv, "creatorsID", typeof (String)); |
||
763 | |||
764 | createCol(inv, "inventoryName", typeof (String)); |
||
765 | createCol(inv, "inventoryDescription", typeof (String)); |
||
766 | // permissions |
||
767 | createCol(inv, "inventoryNextPermissions", typeof (Int32)); |
||
768 | createCol(inv, "inventoryCurrentPermissions", typeof (Int32)); |
||
769 | createCol(inv, "inventoryBasePermissions", typeof (Int32)); |
||
770 | createCol(inv, "inventoryEveryOnePermissions", typeof (Int32)); |
||
771 | createCol(inv, "inventoryGroupPermissions", typeof (Int32)); |
||
772 | |||
773 | // sale info |
||
774 | createCol(inv, "salePrice", typeof(Int32)); |
||
775 | createCol(inv, "saleType", typeof(Byte)); |
||
776 | |||
777 | // creation date |
||
778 | createCol(inv, "creationDate", typeof(Int32)); |
||
779 | |||
780 | // group info |
||
781 | createCol(inv, "groupID", typeof(String)); |
||
782 | createCol(inv, "groupOwned", typeof(Boolean)); |
||
783 | |||
784 | // Flags |
||
785 | createCol(inv, "flags", typeof(UInt32)); |
||
786 | |||
787 | inv.PrimaryKey = new DataColumn[] { inv.Columns["UUID"] }; |
||
788 | return inv; |
||
789 | } |
||
790 | |||
791 | /// <summary> |
||
792 | /// Creates the "inventoryfolders" table |
||
793 | /// </summary> |
||
794 | /// <returns></returns> |
||
795 | private static DataTable createInventoryFoldersTable() |
||
796 | { |
||
797 | DataTable fol = new DataTable("inventoryfolders"); |
||
798 | |||
799 | createCol(fol, "UUID", typeof (String)); //folderID |
||
800 | createCol(fol, "name", typeof (String)); |
||
801 | createCol(fol, "agentID", typeof (String)); |
||
802 | createCol(fol, "parentID", typeof (String)); |
||
803 | createCol(fol, "type", typeof (Int32)); |
||
804 | createCol(fol, "version", typeof (Int32)); |
||
805 | |||
806 | fol.PrimaryKey = new DataColumn[] {fol.Columns["UUID"]}; |
||
807 | return fol; |
||
808 | } |
||
809 | |||
810 | /// <summary> |
||
811 | /// |
||
812 | /// </summary> |
||
813 | /// <param name="da"></param> |
||
814 | /// <param name="conn"></param> |
||
815 | private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) |
||
816 | { |
||
817 | lock (ds) |
||
818 | { |
||
819 | da.InsertCommand = createInsertCommand("inventoryitems", ds.Tables["inventoryitems"]); |
||
820 | da.InsertCommand.Connection = conn; |
||
821 | |||
822 | da.UpdateCommand = createUpdateCommand("inventoryitems", "UUID=:UUID", ds.Tables["inventoryitems"]); |
||
823 | da.UpdateCommand.Connection = conn; |
||
824 | |||
825 | SqliteCommand delete = new SqliteCommand("delete from inventoryitems where UUID = :UUID"); |
||
826 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); |
||
827 | delete.Connection = conn; |
||
828 | da.DeleteCommand = delete; |
||
829 | } |
||
830 | } |
||
831 | |||
832 | /// <summary> |
||
833 | /// |
||
834 | /// </summary> |
||
835 | /// <param name="da"></param> |
||
836 | /// <param name="conn"></param> |
||
837 | private void setupFoldersCommands(SqliteDataAdapter da, SqliteConnection conn) |
||
838 | { |
||
839 | lock (ds) |
||
840 | { |
||
841 | da.InsertCommand = createInsertCommand("inventoryfolders", ds.Tables["inventoryfolders"]); |
||
842 | da.InsertCommand.Connection = conn; |
||
843 | |||
844 | da.UpdateCommand = createUpdateCommand("inventoryfolders", "UUID=:UUID", ds.Tables["inventoryfolders"]); |
||
845 | da.UpdateCommand.Connection = conn; |
||
846 | |||
847 | SqliteCommand delete = new SqliteCommand("delete from inventoryfolders where UUID = :UUID"); |
||
848 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); |
||
849 | delete.Connection = conn; |
||
850 | da.DeleteCommand = delete; |
||
851 | } |
||
852 | } |
||
853 | |||
854 | /// <summary> |
||
855 | /// |
||
856 | /// </summary> |
||
857 | /// <param name="row"></param> |
||
858 | /// <returns></returns> |
||
859 | private static InventoryFolderBase buildFolder(DataRow row) |
||
860 | { |
||
861 | InventoryFolderBase folder = new InventoryFolderBase(); |
||
862 | folder.ID = new UUID((string) row["UUID"]); |
||
863 | folder.Name = (string) row["name"]; |
||
864 | folder.Owner = new UUID((string) row["agentID"]); |
||
865 | folder.ParentID = new UUID((string) row["parentID"]); |
||
866 | folder.Type = Convert.ToInt16(row["type"]); |
||
867 | folder.Version = Convert.ToUInt16(row["version"]); |
||
868 | return folder; |
||
869 | } |
||
870 | |||
871 | /// <summary> |
||
872 | /// |
||
873 | /// </summary> |
||
874 | /// <param name="row"></param> |
||
875 | /// <param name="folder"></param> |
||
876 | private static void fillFolderRow(DataRow row, InventoryFolderBase folder) |
||
877 | { |
||
878 | row["UUID"] = folder.ID.ToString(); |
||
879 | row["name"] = folder.Name; |
||
880 | row["agentID"] = folder.Owner.ToString(); |
||
881 | row["parentID"] = folder.ParentID.ToString(); |
||
882 | row["type"] = folder.Type; |
||
883 | row["version"] = folder.Version; |
||
884 | } |
||
885 | |||
886 | /// <summary> |
||
887 | /// |
||
888 | /// </summary> |
||
889 | /// <param name="row"></param> |
||
890 | /// <param name="folder"></param> |
||
891 | private static void moveFolderRow(DataRow row, InventoryFolderBase folder) |
||
892 | { |
||
893 | row["UUID"] = folder.ID.ToString(); |
||
894 | row["parentID"] = folder.ParentID.ToString(); |
||
895 | } |
||
896 | |||
897 | public List<InventoryItemBase> fetchActiveGestures (UUID avatarID) |
||
898 | { |
||
899 | lock (ds) |
||
900 | { |
||
901 | List<InventoryItemBase> items = new List<InventoryItemBase>(); |
||
902 | |||
903 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; |
||
904 | string selectExp |
||
905 | = "avatarID = '" + avatarID + "' AND assetType = " + (int)AssetType.Gesture + " AND flags = 1"; |
||
906 | //m_log.DebugFormat("[SQL]: sql = " + selectExp); |
||
907 | DataRow[] rows = inventoryItemTable.Select(selectExp); |
||
908 | foreach (DataRow row in rows) |
||
909 | { |
||
910 | items.Add(buildItem(row)); |
||
911 | } |
||
912 | return items; |
||
913 | } |
||
914 | } |
||
915 | } |
||
916 | } |