using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LiteDB;
namespace Firebase.Database.Offline
{
///
/// The offline database.
///
public class ConcurrentOfflineDatabase : IDictionary
{
private readonly ConcurrentDictionary ccache;
private readonly LiteRepository db;
///
/// Initializes a new instance of the class.
///
/// The item type which is used to determine the database file name.
/// Custom string which will get appended to the file name.
public ConcurrentOfflineDatabase(Type itemType, string filenameModifier)
{
var fullName = GetFileName(itemType.ToString());
if (fullName.Length > 100) fullName = fullName.Substring(0, 100);
var mapper = BsonMapper.Global;
mapper.Entity().Id(o => o.Key);
var root = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var filename = fullName + filenameModifier + ".db";
var path = Path.Combine(root, filename);
db = new LiteRepository(new LiteDatabase(path, mapper));
var cache = db.Database
.GetCollection()
.FindAll()
.ToDictionary(o => o.Key, o => o);
ccache = new ConcurrentDictionary(cache);
}
///
/// Gets the number of elements contained in the .
///
/// The number of elements contained in the .
public int Count => ccache.Count;
///
/// Gets a value indicating whether this is a read-only collection.
///
public bool IsReadOnly => false;
///
/// Gets an containing the keys of the
/// .
///
///
/// An containing the keys of the object that
/// implements .
///
public ICollection Keys => ccache.Keys;
///
/// Gets an containing the values in the
/// .
///
///
/// An containing the values in the object that
/// implements .
///
public ICollection Values => ccache.Values;
///
/// Gets or sets the element with the specified key.
///
/// The key of the element to get or set.
/// The element with the specified key.
public OfflineEntry this[string key]
{
get => ccache[key];
set
{
ccache.AddOrUpdate(key, value, (k, existing) => value);
db.Upsert(value);
}
}
///
/// Returns an enumerator that iterates through the collection.
///
/// An enumerator that can be used to iterate through the collection.
public IEnumerator> GetEnumerator()
{
return ccache.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Adds an item to the .
///
/// The object to add to the .
public void Add(KeyValuePair item)
{
Add(item.Key, item.Value);
}
///
/// Removes all items from the .
///
public void Clear()
{
ccache.Clear();
db.Delete(LiteDB.Query.All());
}
///
/// Determines whether the contains a specific value.
///
/// The object to locate in the .
///
/// True if is found in the ;
/// otherwise, false.
///
public bool Contains(KeyValuePair item)
{
return ContainsKey(item.Key);
}
///
/// Copies the elements of the to an
/// , starting at a particular index.
///
///
/// The one-dimensional that is the destination of the elements copied
/// from . The must have
/// zero-based indexing.
///
/// The zero-based index in at which copying begins.
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
ccache.ToList().CopyTo(array, arrayIndex);
}
///
/// Removes the first occurrence of a specific object from the
/// .
///
/// The object to remove from the .
///
/// True if was successfully removed from the
/// ; otherwise, false. This method also returns false if
/// is not found in the original .
///
public bool Remove(KeyValuePair item)
{
return Remove(item.Key);
}
///
/// Determines whether the contains an element with the
/// specified key.
///
/// The key to locate in the .
///
/// True if the contains an element with the key;
/// otherwise, false.
///
public bool ContainsKey(string key)
{
return ccache.ContainsKey(key);
}
///
/// Adds an element with the provided key and value to the .
///
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
public void Add(string key, OfflineEntry value)
{
ccache.AddOrUpdate(key, value, (k, existing) => value);
db.Upsert(value);
}
///
/// Removes the element with the specified key from the .
///
/// The key of the element to remove.
///
/// True if the element is successfully removed; otherwise, false. This method also returns false if
/// was not found in the original .
///
public bool Remove(string key)
{
ccache.TryRemove(key, out _);
return db.Delete(key);
}
///
/// Gets the value associated with the specified key.
///
/// The key whose value to get.
///
/// When this method returns, the value associated with the specified key, if the key is found;
/// otherwise, the default value for the type of the parameter. This parameter is passed
/// uninitialized.
///
///
/// True if the object that implements contains an
/// element with the specified key; otherwise, false.
///
public bool TryGetValue(string key, out OfflineEntry value)
{
return ccache.TryGetValue(key, out value);
}
private string GetFileName(string fileName)
{
var invalidChars = new[] {'`', '[', ',', '='};
foreach (var c in invalidChars.Concat(Path.GetInvalidFileNameChars()).Distinct())
fileName = fileName.Replace(c, '_');
return fileName;
}
}
}