Initial Commit

master
Harald Christian Joachim Wolff 2018-10-16 21:03:23 +02:00
commit a4108ac920
25 changed files with 1887 additions and 0 deletions

41
.gitignore vendored 100644
View File

@ -0,0 +1,41 @@
# Autosave files
*~
# build
[Oo]bj/
[Bb]in/
packages/
TestResults/
# globs
Makefile.in
*.DS_Store
*.sln.cache
*.suo
*.cache
*.pidb
*.userprefs
*.usertasks
config.log
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.user
*.tar.gz
tarballs/
test-results/
Thumbs.db
.vs/
# Mac bundle stuff
*.dmg
*.app
# resharper
*_Resharper.*
*.Resharper
# dotCover
*.dotCover

186
OODB.cs 100644
View File

@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using oodb.descriptor;
using oodb.mapping;
using oodb.persistence;
using System.IO;
using System.Linq;
using System.Xml;
using System.CodeDom;
using System.ComponentModel;
using System.Reflection;
using oodb.mapping.collections;
namespace oodb
{
public class OODB
{
public PersistenceCache PersistenceCache { get; }
public StorageAdapter StorageAdapter { get; }
internal Dictionary<Type, Descriptor> descriptors = new Dictionary<Type, Descriptor>();
internal Dictionary<Type, FlatTypeMapping> flatTypeMappings = new Dictionary<Type, FlatTypeMapping>();
public OODB(String storagePath)
:this(new DirectoryInfo(storagePath))
{
}
public OODB(DirectoryInfo storageDirectory)
{
PersistenceCache = new PersistenceCache(this);
StorageAdapter = new StorageAdapter(this,storageDirectory);
PopulateFlatMappings();
AppDomain.CurrentDomain.ProcessExit += (sender, e) => this.Close();
}
public void Close()
{
StorageAdapter.Close();
}
public void Prepare()
{
foreach (Descriptor descriptor in descriptors.Values)
{
descriptor.Initialize();
}
}
public OODB Add<T>() where T : Persistent => Add(typeof(T));
public OODB Add(Type type)
{
GetDescriptor(type);
return this;
}
public void Save(IEnumerable<Persistent> persistents)
{
foreach (Persistent persistent in persistents)
Save(persistent);
}
public void Save(Persistent o)
{
PersistenceCache.Touch(o);
StorageAdapter.Save(o);
}
public void Ensure(Persistent o)
{
if (!StorageAdapter.Knows(o.GetType(), o.PersistenceID))
Save(o);
}
public T Load<T>(Guid persistenceID) where T: Persistent
{
return (T)Load(typeof(T), persistenceID);
}
public Persistent Load(Type type,Guid persistenceID)
{
if (persistenceID.Equals(Guid.Empty))
return null;
try {
return PersistenceCache.Get(type, persistenceID);
} catch (KeyNotFoundException)
{
Type realType = StorageAdapter.GetDiscriminator(type, persistenceID);
Persistent persistent = Activator.CreateInstance(type,true) as Persistent;
persistent.PersistenceID = persistenceID;
PersistenceCache.Touch(persistent);
GetDescriptor(realType).Attach(persistent);
StorageAdapter.Restore(persistent);
return persistent;
}
}
public IEnumerable<T> List<T>() where T:Persistent => List(typeof(T)).Select((x) => (T)x);
public IEnumerable<Persistent> List(Type type)
{
return ListIdentities(type).Select((x) => Load(x.Value, x.Key));
}
private IEnumerable<KeyValuePair<Guid,Type>> ListIdentities(Type type)
{
return StorageAdapter.List(GetDescriptor(type));
}
public Type LookupType(string fullName)
{
foreach (Type t in descriptors.Keys)
if (t.FullName.Equals(fullName))
return t;
throw new KeyNotFoundException(fullName);
}
public Descriptor GetDescriptor(Type type)
{
if (!descriptors.ContainsKey(type))
{
descriptors[type] = new Descriptor(this, type);
}
return descriptors[type];
}
public Descriptor GetDescriptor<T>() => GetDescriptor(typeof(T));
public FlatTypeMapping GetFlatTypeMapping(Type type)
{
if (!flatTypeMappings.ContainsKey(type))
{
if (type.IsArray)
{
flatTypeMappings[type] = new ArrayMapping(this, type);
}
else if (type.IsSubclassOf(typeof(Persistent)))
{
Add(type);
}
else if (type.IsGenericType && ((type.GetGenericTypeDefinition() == typeof(List<>)) || (type.GetGenericTypeDefinition() == typeof(IList<>))))
{
FlatTypeMapping elementMapping = GetFlatTypeMapping(type.GetGenericArguments()[0]);
flatTypeMappings[type] = new ListMapping(this, type);
} else {
throw new KeyNotFoundException();
}
}
return flatTypeMappings[type];
}
public ExtendedFieldHandling CreateExtendedFieldHandling(FieldInfo fieldInfo)
{
if (fieldInfo.FieldType.IsGenericType){
Type genericTypeDefinition = fieldInfo.FieldType.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(ISet<>))
{
return new ISetExtendedHandling(this, fieldInfo);
}
}
throw new NotSupportedException();
}
private void PopulateFlatMappings()
{
flatTypeMappings[typeof(string)] = new StringMapping(this);
flatTypeMappings[typeof(int)] = new IntegerMapping(this);
flatTypeMappings[typeof(short)] = new IntegerMapping(this);
flatTypeMappings[typeof(byte)] = new IntegerMapping(this);
flatTypeMappings[typeof(float)] = new DoubleMapping(this);
flatTypeMappings[typeof(double)] = new DoubleMapping(this);
}
}
}

13
Persistent.cs 100644
View File

@ -0,0 +1,13 @@
using System;
namespace oodb
{
public abstract class Persistent
{
public Guid PersistenceID { get; internal set; }
public Persistent()
{
PersistenceID = Guid.NewGuid();
}
}
}

View File

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("sharp-oodb")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("${AuthorCopyright}")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

221
Query.cs 100644
View File

@ -0,0 +1,221 @@
using System;
using oodb.descriptor;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Metadata;
using System.Runtime.Remoting.Messaging;
using System.Collections;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Xsl.Runtime;
using oodb.index;
namespace oodb
{
public class QueryField
{
public string FieldName { get; }
public QueryField(String fieldName)
{
FieldName = fieldName;
}
public Predicate Contains(object value)
{
return new Predicate.Contains(this, value);
}
}
public abstract class Predicate
{
public QueryField QueryField { get; }
public Predicate(QueryField queryField)
{
QueryField = queryField;
}
public virtual bool MatchesTextValues => false;
public virtual bool TextMatch(string textValue) { throw new NotSupportedException(); }
public abstract bool Match(object value);
public OrPredicate Or => new OrPredicate(this);
public class OrPredicate : Predicate
{
public HashSet<Predicate> Predicates { get; } = new HashSet<Predicate>();
public OrPredicate(Predicate creator)
:base(creator.QueryField)
{
Predicates.Add(creator);
}
public OrPredicate Contains(object value)
{
Predicates.Add(new Predicate.Contains(this.QueryField, value));
return this;
}
public override bool MatchesTextValues
{
get {
foreach (Predicate predicate in Predicates)
if (!predicate.MatchesTextValues)
return false;
return true;
}
}
public override bool TextMatch(string textValue)
{
foreach (Predicate predicate in Predicates)
if (predicate.TextMatch(textValue))
return true;
return false;
}
public override bool Match(object value)
{
foreach (Predicate predicate in Predicates)
if (predicate.Match(value))
return true;
return false;
}
}
public class Contains : Predicate
{
public object Value { get; }
public Contains(QueryField queryField,object value)
:base(queryField)
{
Value = value;
}
public override bool Match(object value)
{
if ((Value == null) || String.Empty.Equals(Value))
return true;
if (value == null)
return false;
return (value as string).Contains(Value as string);
}
}
}
public class Query<T> where T: Persistent
{
public virtual AlternativesQuery<T> Or(Query<T> alternativeQuery) => new AlternativesQuery<T>(this, alternativeQuery);
public virtual AlternativesQuery<T> Or(params Predicate[] predicates) => new AlternativesQuery<T>(this, new Query<T>(predicates));
public HashSet<Predicate> Predicates { get; } = new HashSet<Predicate>();
protected Query()
{
}
public Query(IEnumerable<Predicate> predicates)
{
foreach (Predicate predicate in predicates)
{
Predicates.Add(predicate);
}
}
public Query(params Predicate[] predicates)
{
foreach (Predicate predicate in predicates)
{
Predicates.Add(predicate);
}
}
public IEnumerable<Predicate> GetPredicates(QueryField queryField)
{
return Predicates.Where((x) => x.QueryField == queryField);
}
public virtual IEnumerable<T> Search(OODB oodb)
{
Descriptor descriptor = oodb.GetDescriptor<T>();
HashSet<QueryField> predicateFields = new HashSet<QueryField>();
foreach (Predicate predicate in Predicates)
{
predicateFields.Add(predicate.QueryField);
}
Guid[] results = null;
foreach (QueryField queryField in predicateFields)
{
FieldInfo fieldInfo = descriptor.GetFieldInfo(queryField.FieldName);
if (!oodb.StorageAdapter.HasSearchIndex(fieldInfo))
throw new NotSupportedException("Queries must use indexed fields");
SearchIndex searchIndex = oodb.StorageAdapter.GetSearchIndex(fieldInfo);
foreach (Predicate predicate in GetPredicates(queryField))
{
IEnumerable<Guid> myresults = searchIndex.Query(predicate);
if (results == null)
{
results = myresults.ToArray();
} else {
results = results.Intersect(myresults).ToArray();
}
}
}
return results.Select((id)=>oodb.Load<T>(id));
}
}
public class AlternativesQuery<T> : Query<T> where T:Persistent
{
public HashSet<Query<T>> Alternatives = new HashSet<Query<T>>();
public AlternativesQuery(params Query<T>[] queries)
{
foreach (Query<T> alternative in queries)
Alternatives.Add(alternative);
}
public override AlternativesQuery<T> Or(Query<T> alternativeQuery)
{
Alternatives.Add(alternativeQuery);
return this;
}
public override AlternativesQuery<T> Or(params Predicate[] predicates)
{
Alternatives.Add(new Query<T>(predicates));
return this;
}
public override IEnumerable<T> Search(OODB oodb)
{
return Alternatives.SelectMany((alternative) => alternative.Search(oodb)).Distinct();
}
}
}

271
StorageAdapter.cs 100644
View File

@ -0,0 +1,271 @@
using System;
using oodb.descriptor;
using System.Collections.Generic;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Xml;
using oodb.mapping;
using sharp.logging;
using System.Linq;
using oodb.index;
namespace oodb
{
public class StorageAdapter
{
public OODB OODB { get; }
public DirectoryInfo BaseDirectory { get; }
Dictionary<Type, TypeStorage> typeStorages = new Dictionary<Type, TypeStorage>();
public StorageAdapter(OODB oodb,DirectoryInfo baseDirectory)
{
OODB = oodb;
BaseDirectory = baseDirectory;
if (!BaseDirectory.Exists)
BaseDirectory.Create();
}
public void Close()
{
foreach (KeyValuePair<Type,TypeStorage> kvp in typeStorages)
{
foreach (Index index in kvp.Value.indeces)
index.Save();
}
}
public void Save(Persistent persistent)
{
GetTypeStorage(persistent.GetType()).Save(persistent);
}
public void Restore(Persistent persistent)
{
GetTypeStorage(persistent.GetType()).Restore(persistent);
}
public bool Knows(Type type,Guid persistenceID)
{
return GetTypeStorage(type).Knows(persistenceID);
}
public void Reference(Persistent persistent, FieldInfo referencing)
{
GetTypeStorage(persistent.GetType()).Reference(persistent, referencing);
}
public void UnReference(Persistent persistent, FieldInfo referencing)
{
GetTypeStorage(persistent.GetType()).UnReference(persistent, referencing);
}
public IEnumerable<KeyValuePair<Guid,Type>> List(Descriptor descriptor)
{
return GetTypeStorage(descriptor.NativeType).List();
}
public Type GetDiscriminator(Type type,Guid persistenceID)
{
return GetTypeStorage(type).GetDiscriminator(persistenceID);
}
public SearchIndex GetSearchIndex(FieldInfo fieldInfo)
{
return GetTypeStorage(fieldInfo.DeclaringType).GetSearchIndex(fieldInfo);
}
public bool HasSearchIndex(FieldInfo fieldInfo)
{
return GetTypeStorage(fieldInfo.DeclaringType).HasSearchIndex(fieldInfo);
}
public TypeStorage GetTypeStorage(Type type)
{
if (!typeStorages.ContainsKey(type))
{
typeStorages[type] = new TypeStorage(this, type);
}
return typeStorages[type];
}
public class TypeStorage
{
public StorageAdapter StorageAdapter { get; }
public Type PersistentType { get; }
public DirectoryInfo Directory { get; }
public Descriptor Descriptor { get; }
public TypeStorage BaseStorage { get; }
public HashSet<Index> indeces = new HashSet<Index>();
public Dictionary<FieldInfo, SearchIndex> searchIndeces = new Dictionary<FieldInfo, SearchIndex>();
public TypeStorage(StorageAdapter storageAdapter, Type type)
{
StorageAdapter = storageAdapter;
PersistentType = type;
Directory = new DirectoryInfo(Path.Combine(StorageAdapter.BaseDirectory.FullName, type.FullName));
if (!Directory.Exists)
Directory.Create();
Descriptor = storageAdapter.OODB.GetDescriptor(type);
if (Descriptor.BaseDescriptor != null)
{
BaseStorage = StorageAdapter.GetTypeStorage(Descriptor.BaseDescriptor.NativeType);
}
}
public bool AddIndex(Index index)
{
return indeces.Add(index);
}
public bool HasSearchIndex(FieldInfo fieldInfo) => searchIndeces.ContainsKey(fieldInfo);
public SearchIndex GetSearchIndex(FieldInfo fieldInfo)
{
if (!searchIndeces.ContainsKey(fieldInfo))
{
SearchIndex searchIndex = new SearchIndex(StorageAdapter.OODB, fieldInfo);
searchIndeces.Add(fieldInfo, searchIndex);
}
return searchIndeces[fieldInfo];
}
private void RemoveDiscriminator(Guid persistenceID)
{
foreach (Index index in indeces)
{
index.Remove(persistenceID);
}
File.Delete(Path.Combine(Directory.FullName, String.Format("{0}.discriminator", persistenceID.ToString())));
if (BaseStorage != null)
BaseStorage.RemoveDiscriminator(persistenceID);
}
private String ReadDiscriminator(Guid persistenceID)
{
byte[] discriminator = File.ReadAllBytes(Path.Combine(Directory.FullName, String.Format("{0}.discriminator", persistenceID.ToString())));
return Encoding.UTF8.GetString(discriminator);
}
private void WriteDiscriminator(Persistent persistent)
{
foreach (Index index in indeces)
{
index.Add(persistent);
}
using (FileStream fileStream = new FileStream(Path.Combine(Directory.FullName,String.Format("{0}.discriminator",persistent.PersistenceID.ToString())),FileMode.Create))
{
byte[] discriminator = Encoding.UTF8.GetBytes(persistent.GetType().FullName);
fileStream.Write(discriminator, 0, discriminator.Length);
fileStream.Close();
}
if (BaseStorage != null)
BaseStorage.WriteDiscriminator(persistent);
}
public bool Knows(Guid persistenceID)
{
return File.Exists(Path.Combine(Directory.FullName, String.Format("{0}.discriminator", persistenceID.ToString())));
}
public void Save(Persistent persistent)
{
try {
WriteDiscriminator(persistent);
} catch (Exception e)
{
Logger.Default.Log(e);
RemoveDiscriminator(persistent.PersistenceID);
throw e;
}
try
{
Descriptor descriptor = StorageAdapter.OODB.GetDescriptor(persistent.GetType());
XmlDocument xmlDocument = new XmlDocument();
XmlElement persistetInstance = xmlDocument.CreateElement("PersistetInstance");
xmlDocument.AppendChild(persistetInstance);
foreach (KeyValuePair<FieldInfo, FlatTypeMapping> flatField in descriptor.FlatMappings)
{
XmlElement fieldElement = xmlDocument.CreateElement("Field");
fieldElement.SetAttribute("Name", flatField.Key.Name);
fieldElement.AppendChild(flatField.Value.ToXml(xmlDocument, flatField.Key.GetValue(persistent)));
persistetInstance.AppendChild(fieldElement);
}
xmlDocument.Save(Path.Combine(Directory.FullName, String.Format("{0}.xml", persistent.PersistenceID.ToString())));
} catch (Exception e)
{
Logger.Default.Log(LogLevel.ERROR, "StorageAdapter.Save(): {0}", e);
}
}
public void Restore(Persistent persistent)
{
Descriptor descriptor = StorageAdapter.OODB.GetDescriptor(persistent.GetType());
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(Path.Combine(Directory.FullName, String.Format("{0}.xml", persistent.PersistenceID.ToString())));
foreach (XmlElement fieldElement in xmlDocument.SelectNodes("PersistetInstance/Field"))
{
try {
FieldInfo fieldInfo = Descriptor.GetFieldInfo(fieldElement.GetAttribute("Name"));
FlatTypeMapping flatTypeMapping = Descriptor.GetFlatTypeMapping(fieldInfo);
fieldInfo.SetValue(persistent, flatTypeMapping.FromXml(fieldElement.SelectSingleNode("Value") as XmlElement));
} catch (KeyNotFoundException)
{
Logger.Default.Log(LogLevel.WARNING, "data for unknown field {0} ignored", fieldElement.GetAttribute("Name"));
}
}
}
public void Reference(Persistent persistent, FieldInfo referencing)
{
throw new NotSupportedException();
}
public void UnReference(Persistent persistent, FieldInfo referencing)
{
throw new NotSupportedException();
}
public IEnumerable<KeyValuePair<Guid,Type>> List()
{
KeyValuePair<Guid,Type> create(FileInfo fileInfo)
{
Guid persistenceID = Guid.Parse(fileInfo.Name.Substring(0, fileInfo.Name.Length - fileInfo.Extension.Length));
return new KeyValuePair<Guid, Type>(persistenceID,GetDiscriminator(persistenceID));
}
return Directory.GetFiles("*.discriminator").Select(create);
}
public Type GetDiscriminator(Guid persistenceID)
{
String typeName = ReadDiscriminator(persistenceID);
return StorageAdapter.OODB.LookupType(typeName);
}
}
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace oodb.attributes
{
public class IndexedAttribute : Attribute
{
public IndexedAttribute()
{
}
}
}

View File

@ -0,0 +1,13 @@
using System;
namespace oodb.attributes
{
public class ReferencedFieldAttribute : Attribute
{
public String FieldName { get; }
public ReferencedFieldAttribute(String FieldName)
{
this.FieldName = FieldName;
}
}
}

View File

@ -0,0 +1,10 @@
using System;
namespace oodb.attributes
{
public class UniqueAttribute : Attribute
{
public UniqueAttribute()
{
}
}
}

View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using oodb.mapping;
using System.Linq.Expressions;
using sharp.logging;
using oodb.attributes;
using oodb.index;
namespace oodb.descriptor
{
public class Descriptor
{
public OODB OODB { get; }
public Type NativeType { get; }
public Descriptor BaseDescriptor { get; }
public IEnumerable<KeyValuePair<FieldInfo, FlatTypeMapping>> FlatMappings => flatMappings;
Dictionary<FieldInfo, FlatTypeMapping> flatMappings = new Dictionary<FieldInfo, FlatTypeMapping>();
Dictionary<FieldInfo, ExtendedFieldHandling> extendedFieldHandlers = new Dictionary<FieldInfo, ExtendedFieldHandling>();
public Descriptor(OODB oodb,Type type)
{
OODB = oodb;
NativeType = type;
OODB.descriptors.Add(NativeType, this);
OODB.flatTypeMappings[type] = new ReferenceMapping(oodb, type);
if (type.BaseType != typeof(Persistent))
{
BaseDescriptor = OODB.GetDescriptor(type.BaseType);
}
ConstructMappings();
}
public void Initialize()
{
foreach (ExtendedFieldHandling extendedFieldHandling in extendedFieldHandlers.Values)
{
extendedFieldHandling.Initialize();
}
}
public void Attach(Persistent persistent)
{
foreach (ExtendedFieldHandling extendedFieldHandling in extendedFieldHandlers.Values)
{
extendedFieldHandling.Attach(persistent);
}
}
public void Detach(Persistent persistent)
{
foreach (ExtendedFieldHandling extendedFieldHandling in extendedFieldHandlers.Values)
{
extendedFieldHandling.Detach(persistent);
}
}
public FieldInfo GetFieldInfo(string fieldName)
{
foreach (FieldInfo fieldInfo in flatMappings.Keys)
if (fieldInfo.Name.Equals(fieldName))
return fieldInfo;
throw new KeyNotFoundException();
}
public FlatTypeMapping GetFlatTypeMapping(FieldInfo fieldInfo)
{
return flatMappings[fieldInfo];
}
private void ConstructMappings()
{
if (BaseDescriptor != null)
{
foreach (FieldInfo baseField in BaseDescriptor.flatMappings.Keys)
{
flatMappings.Add(baseField, BaseDescriptor.flatMappings[baseField]);
}
foreach (FieldInfo baseField in BaseDescriptor.extendedFieldHandlers.Keys)
{
extendedFieldHandlers.Add(baseField, BaseDescriptor.extendedFieldHandlers[baseField]);
}
}
foreach (FieldInfo fieldInfo in NativeType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic))
{
if (fieldInfo.GetCustomAttribute<IndexedAttribute>() != null)
{
OODB.StorageAdapter.GetSearchIndex(fieldInfo);
}
try
{
FlatTypeMapping flatTypeMapping = OODB.GetFlatTypeMapping(fieldInfo.FieldType);
flatMappings.Add(fieldInfo, flatTypeMapping);
if (fieldInfo.GetCustomAttribute<UniqueAttribute>() != null)
{
Unique uniqueIndex = new Unique(OODB, fieldInfo);
}
} catch (KeyNotFoundException)
{
try {
ExtendedFieldHandling extendedFieldHandling = OODB.CreateExtendedFieldHandling(fieldInfo);
extendedFieldHandlers.Add(fieldInfo, extendedFieldHandling);
} catch (NotSupportedException)
{
Logger.Default.Log("Ignoring unsupported Field {0}", fieldInfo);
}
}
}
}
}
}

111
index/Index.cs 100644
View File

@ -0,0 +1,111 @@
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using oodb.mapping;
using System.IO;
using System.Reflection.Emit;
using System.Linq;
namespace oodb.index
{
public abstract class Index
{
public OODB OODB { get; }
public FieldInfo FieldInfo { get; }
public String FileName { get; }
public FlatTypeMapping ValueMapping { get; }
public FlatTypeMapping StorageMapping { get; protected set; }
public Index(OODB oodb, FieldInfo fieldInfo)
{
OODB = oodb;
FieldInfo = fieldInfo;
ValueMapping = OODB.GetFlatTypeMapping(fieldInfo.FieldType);
StorageMapping = ValueMapping;
FileName = Path.Combine(
OODB.StorageAdapter.GetTypeStorage(FieldInfo.DeclaringType).Directory.FullName,
String.Format("index_{0}_{1}_{2}.xml", GetType().Name, FieldInfo.DeclaringType.Name, FieldInfo.Name)
);
OODB.StorageAdapter.GetTypeStorage(fieldInfo.DeclaringType).AddIndex(this);
Prepare();
Load();
}
protected abstract void Prepare();
public abstract IEnumerable<KeyValuePair<Guid, object>> RetrieveIndexValues();
public abstract void RestoreIndexValues(IEnumerable<KeyValuePair<Guid, object>> indexValues);
public abstract void Add(Persistent persistent);
public abstract void Remove(Guid persistenceID);
public void Remove(Persistent persistent)
{
Remove(persistent.PersistenceID);
}
public abstract IEnumerable<Guid> Query();
public void Load()
{
if (File.Exists(FileName))
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(FileName);
RestoreIndexValues(
xmlDocument.SelectNodes("/Index/Entry").Cast<XmlElement>().Select((entry) => new KeyValuePair<Guid, object>(Guid.Parse(entry.GetAttribute("PersistenceID")), StorageMapping.FromXml(entry.SelectSingleNode("Value") as XmlElement)))
);
}
}
public void Save()
{
XmlDocument xmlDocument = new XmlDocument();
XmlElement indexElement = xmlDocument.CreateElement("Index");
indexElement.SetAttribute("Type", GetType().FullName);
xmlDocument.AppendChild(indexElement);
foreach (KeyValuePair<Guid,object> entry in RetrieveIndexValues())
{
XmlElement xmlEntry = xmlDocument.CreateElement("Entry");
xmlEntry.SetAttribute("PersistenceID", entry.Key.ToString());
xmlEntry.AppendChild(StorageMapping.ToXml(xmlDocument, entry.Value));
indexElement.AppendChild(xmlEntry);
}
xmlDocument.Save(FileName);
}
public override int GetHashCode()
{
return FileName.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is Index)
{
Index other = obj as Index;
return FileName.Equals(other.FileName);
}
return false;
}
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using oodb.mapping;
using System.Linq;
using System.Collections;
namespace oodb.index
{
public class SearchIndex : Index
{
Dictionary<string, ISet<Guid>> index = new Dictionary<string, ISet<Guid>>();
Dictionary<Guid, string> reverseIndex = new Dictionary<Guid, string>();
public SearchIndex(OODB oodb,FieldInfo fieldInfo)
:base(oodb,fieldInfo)
{
}
protected override void Prepare()
{
StorageMapping = OODB.GetFlatTypeMapping(typeof(string));
}
public ISet<Guid> GetBucket(string value)
{
if (!index.ContainsKey(value))
{
index.Add(value, new HashSet<Guid>());
}
return index[value];
}
public void Add(string value,Guid persistenceID)
{
if (reverseIndex.ContainsKey(persistenceID))
{
if (!reverseIndex[persistenceID].Equals(value))
{
GetBucket(reverseIndex[persistenceID]).Remove(persistenceID);
GetBucket(value).Add(persistenceID);
reverseIndex[persistenceID] = value;
}
} else {
reverseIndex[persistenceID] = value;
GetBucket(value).Add(persistenceID);
}
}
public override void Add(Persistent persistent)
{
Add(ValueMapping.ToText(FieldInfo.GetValue(persistent)), persistent.PersistenceID);
}
public override IEnumerable<Guid> Query()
{
return reverseIndex.Keys;
}
public IEnumerable<Guid> QueryTextValue(string textValue)
{
return GetBucket(textValue);
}
public IEnumerable<Guid> Query(object value)
{
return QueryTextValue(ValueMapping.ToText(value));
}
public IEnumerable<Guid> Query(Predicate predicate)
{
if (predicate.MatchesTextValues)
{
return reverseIndex.Where((kvp) => predicate.TextMatch(kvp.Value)).Select((kvp) => kvp.Key);
} else
{
return reverseIndex.Where((kvp) => predicate.Match(ValueMapping.FromText(kvp.Value))).Select((kvp) => kvp.Key);
}
}
public override void Remove(Guid persistenceID)
{
if (reverseIndex.ContainsKey(persistenceID))
{
GetBucket(reverseIndex[persistenceID]).Remove(persistenceID);
reverseIndex.Remove(persistenceID);
}
}
public override void RestoreIndexValues(IEnumerable<KeyValuePair<Guid, object>> indexValues)
{
reverseIndex.Clear();
index.Clear();
foreach (KeyValuePair<Guid,object> kvp in indexValues)
{
Add(kvp.Value as string, kvp.Key);
}
}
public override IEnumerable<KeyValuePair<Guid, object>> RetrieveIndexValues()
{
return reverseIndex.Select((x) => new KeyValuePair<Guid, object>(x.Key, x.Value));
}
}
}

73
index/Unique.cs 100644
View File

@ -0,0 +1,73 @@
using System;
using System.Reflection;
using System.Collections.Generic;
namespace oodb.index
{
public class Unique : Index
{
Dictionary<Guid, object> uniqueForward = new Dictionary<Guid, object>();
Dictionary<object, Guid> uniqueReverse = new Dictionary<object, Guid>();
public Unique(OODB oodb,FieldInfo fieldInfo)
:base(oodb,fieldInfo)
{
}
protected override void Prepare()
{
}
public override void Add(Persistent persistent)
{
object value = FieldInfo.GetValue(persistent);
Guid persistenceId = persistent.PersistenceID;
if (uniqueReverse.ContainsKey(value))
{
if (uniqueReverse[value].Equals(persistenceId))
return;
throw new ArgumentException();
}
if (uniqueForward.ContainsKey(persistenceId))
Remove(persistenceId);
uniqueForward.Add(persistenceId, value);
uniqueReverse.Add(value, persistenceId);
}
public override IEnumerable<Guid> Query()
{
throw new NotImplementedException();
}
public override void Remove(Guid persistenceID)
{
if (uniqueForward.ContainsKey(persistenceID))
{
uniqueReverse.Remove(uniqueForward[persistenceID]);
uniqueForward.Remove(persistenceID);
}
}
public override void RestoreIndexValues(IEnumerable<KeyValuePair<Guid, object>> indexValues)
{
uniqueForward.Clear();
uniqueReverse.Clear();
foreach (KeyValuePair<Guid,object> entry in indexValues)
{
uniqueForward.Add(entry.Key, entry.Value);
uniqueReverse.Add(entry.Value, entry.Key);
}
}
public override IEnumerable<KeyValuePair<Guid, object>> RetrieveIndexValues()
{
return uniqueForward;
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Xml;
using System.Collections.Generic;
using System.Collections;
namespace oodb.mapping
{
public class ArrayMapping : ListMapping
{
public ArrayMapping(OODB oodb,Type type)
:base(oodb,typeof(List<>).MakeGenericType(type.GetElementType()))
{
}
public override XmlElement ToXml(XmlDocument xmlDocument, object value)
{
IList list = value as IList;
Array array = Array.CreateInstance(ElementMapping.NativeType, list.Count);
(list as IList).CopyTo(array, 0);
return base.ToXml(xmlDocument,list);
}
public override object FromXml(XmlElement xmlValue)
{
IList list = base.FromXml(xmlValue) as IList;
Array array = Array.CreateInstance(ElementMapping.NativeType, list.Count) as Array;
list.CopyTo(array, list.Count);
return array;
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Xml;
using System.Globalization;
namespace oodb.mapping
{
public class DoubleMapping : NaiveMapping
{
public DoubleMapping(OODB oodb)
: base(oodb, typeof(double))
{
}
public override object FromText(string text)
{
double iValue = double.Parse(text, CultureInfo.InvariantCulture);
if (NativeType != typeof(double))
{
return Convert.ChangeType(iValue, NativeType);
}
return iValue;
}
public override string ToText(object value)
{
double iValue = (double)value;
return iValue.ToString(CultureInfo.InvariantCulture);
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Reflection;
using System.Xml;
namespace oodb.mapping
{
public abstract class ExtendedFieldHandling
{
public OODB OODB { get; }
public FieldInfo FieldInfo { get; }
public ExtendedFieldHandling(OODB oodb,FieldInfo fieldInfo)
{
OODB = oodb;
FieldInfo = fieldInfo;
}
public virtual void Initialize(){}
public abstract void Save(XmlElement persistenceElement,Persistent persistent);
public abstract void Load(XmlElement persistenceElement,Persistent persistent);
public abstract void Attach(Persistent persistent);
public abstract void Detach(Persistent persistent);
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Xml;
namespace oodb.mapping
{
public abstract class FlatTypeMapping
{
public OODB OODB { get; }
public Type NativeType { get; }
public FlatTypeMapping(OODB oodb, Type nativeType)
{
OODB = oodb;
NativeType = nativeType;
}
public XmlElement CreateEmptyValueElement(XmlDocument xmlDocument)
{
XmlElement value = xmlDocument.CreateElement("Value");
value.SetAttribute("Type", NativeType.FullName);
return value;
}
public abstract XmlElement ToXml(XmlDocument xmlDocument, object value);
public abstract object FromXml(XmlElement xmlValue);
public abstract String ToText(object value);
public abstract object FromText(string text);
}
public abstract class NaiveMapping : FlatTypeMapping
{
public NaiveMapping(OODB oodb, Type nativeType)
:base(oodb,nativeType)
{
}
public override object FromXml(XmlElement xmlValue)
{
if (!NativeType.FullName.Equals(xmlValue.GetAttribute("Type")))
throw new ArgumentException("Type != NativeType", nameof(xmlValue));
return FromText(xmlValue.InnerText);
}
public override XmlElement ToXml(XmlDocument xmlDocument, object value)
{
XmlElement valueElement = CreateEmptyValueElement(xmlDocument);
if (value != null)
valueElement.InnerText = ToText(value);
return valueElement;
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Xml;
using System.Globalization;
namespace oodb.mapping
{
public class IntegerMapping : NaiveMapping
{
public IntegerMapping(OODB oodb)
: base(oodb, typeof(int))
{
}
public override object FromText(String text)
{
int iValue = int.Parse(text, CultureInfo.InvariantCulture);
if (NativeType != typeof(int))
{
return Convert.ChangeType(iValue, NativeType);
}
return iValue;
}
public override string ToText(object value)
{
int iValue = (int)value;
return iValue.ToString(CultureInfo.InvariantCulture);
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
namespace oodb.mapping
{
public class ListMapping : NaiveMapping
{
public FlatTypeMapping ElementMapping { get; }
public ListMapping(OODB oodb, Type type)
: base(oodb, type)
{
ElementMapping = OODB.GetFlatTypeMapping(type.GetGenericArguments()[0]);
}
public override object FromXml(XmlElement xmlValue)
{
if (!NativeType.FullName.Equals(xmlValue.GetAttribute("Type")))
throw new ArgumentException("Type != NativeType", nameof(xmlValue));
IList list = Activator.CreateInstance(typeof(List<>).MakeGenericType(ElementMapping.NativeType)) as IList;
foreach (XmlNode elementNode in xmlValue.ChildNodes)
{
if ((elementNode is XmlElement) && ((((XmlElement)elementNode).Name.Equals("Value"))))
{
list.Add(ElementMapping.FromXml(elementNode as XmlElement));
}
}
return list;
}
public override XmlElement ToXml(XmlDocument xmlDocument, object value)
{
XmlElement valueElement = CreateEmptyValueElement(xmlDocument);
IList list = value as IList;
for (int n = 0; n < list.Count; n++)
{
valueElement.AppendChild(ElementMapping.ToXml(xmlDocument, list[n]));
}
return valueElement;
}
public override string ToText(object value)
{
throw new NotImplementedException();
}
public override object FromText(string text)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Xml;
namespace oodb.mapping
{
public class ReferenceMapping : NaiveMapping
{
public ReferenceMapping(OODB oodb, Type type)
: base(oodb, type)
{
}
public override object FromText(string text)
{
if (String.Empty.Equals(text))
return null;
Guid persistenceID = Guid.Parse(text);
return OODB.Load(NativeType, persistenceID);
}
public override string ToText(object value)
{
Persistent reference = value as Persistent;
if (reference == null)
{
return Guid.Empty.ToString();
}
else
{
string text = reference.PersistenceID.ToString();
OODB.Ensure(reference);
return text;
}
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Xml;
namespace oodb.mapping
{
public class StringMapping : NaiveMapping
{
public StringMapping(OODB oodb)
: base(oodb, typeof(string))
{
}
public override object FromText(string text)
{
return text;
}
public override string ToText(object value)
{
return value as string;
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Reflection;
using System.Xml;
using oodb.attributes;
namespace oodb.mapping.collections
{
public class ISetExtendedHandling : ExtendedFieldHandling
{
public Type ReferencedType { get; }
public FieldInfo ReferencedField { get; protected set; }
public Type ImplementationType { get; }
public ISetExtendedHandling(OODB oodb,FieldInfo fieldInfo)
:base(oodb,fieldInfo)
{
ReferencedType = fieldInfo.FieldType.GetGenericArguments()[0];
ImplementationType = typeof(ISetImplementation<>).MakeGenericType(ReferencedType);
}
public override void Initialize()
{
ReferencedFieldAttribute referencedFieldAttribute = FieldInfo.GetCustomAttribute<ReferencedFieldAttribute>();
string refFieldName = referencedFieldAttribute != null ? referencedFieldAttribute.FieldName : FieldInfo.DeclaringType.Name;
ReferencedField = ReferencedType.GetField(refFieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
OODB.StorageAdapter.GetSearchIndex(ReferencedField);
}
public override void Attach(Persistent persistent)
{
object iset = FieldInfo.GetValue(persistent);
if (ImplementationType != iset.GetType())
{
object impl = Activator.CreateInstance(ImplementationType,OODB,FieldInfo,persistent);
FieldInfo.SetValue(persistent, impl);
}
}
public override void Detach(Persistent persistent)
{
}
public override void Load(XmlElement persistenceElement, Persistent persistent)
{
Attach(persistent);
}
public override void Save(XmlElement persistenceElement, Persistent persistent)
{
}
}
}

View File

@ -0,0 +1,150 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using oodb.attributes;
using oodb.index;
using System.Linq;
namespace oodb.mapping.collections
{
public class ISetImplementation<T> : ISet<T> where T: Persistent
{
public OODB OODB { get; }
public FieldInfo FieldInfo { get; }
public Persistent Owner { get; }
public FieldInfo ReferencedField { get; }
public Type ReferencedType { get; }
public SearchIndex SearchIndex { get; }
public ISetImplementation(OODB oodb,FieldInfo fieldInfo,Persistent owner)
{
OODB = oodb;
FieldInfo = fieldInfo;
Owner = owner;
ReferencedType = fieldInfo.FieldType.GetGenericArguments()[0];
ReferencedFieldAttribute referencedFieldAttribute = FieldInfo.GetCustomAttribute<ReferencedFieldAttribute>();
string refFieldName = referencedFieldAttribute != null ? referencedFieldAttribute.FieldName : FieldInfo.DeclaringType.Name;
ReferencedField = ReferencedType.GetField(refFieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
SearchIndex = OODB.StorageAdapter.GetSearchIndex(ReferencedField);
}
public IEnumerable<Guid> ElementPersistenceIDs => SearchIndex.QueryTextValue(Owner.PersistenceID.ToString());
public int Count => ElementPersistenceIDs.Count();
public bool IsReadOnly => false;
public bool Add(T item)
{
if (!Contains(item))
{
ReferencedField.SetValue(item, Owner);
OODB.Save(item);
return true;
}
return false;
}
public bool Remove(T item)
{
if (Contains(item))
{
ReferencedField.SetValue(item, null);
OODB.Save(item);
return true;
}
return false;
}
public void Clear()
{
foreach (T item in this)
Remove(item);
}
public bool Contains(T item)
{
Persistent persistentItem = item as Persistent;
foreach (Guid elementID in ElementPersistenceIDs)
if (elementID.Equals(persistentItem.PersistenceID))
return true;
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public void ExceptWith(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
return ElementPersistenceIDs.Select((id) => OODB.Load<T>(id)).GetEnumerator();
}
public void IntersectWith(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public bool IsProperSubsetOf(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public bool IsProperSupersetOf(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public bool IsSubsetOf(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public bool IsSupersetOf(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public bool Overlaps(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public bool SetEquals(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public void SymmetricExceptWith(IEnumerable<T> other)
{
throw new NotImplementedException();
}
public void UnionWith(IEnumerable<T> other)
{
throw new NotImplementedException();
}
void ICollection<T>.Add(T item)
{
Add(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using oodb.descriptor;
using System.Dynamic;
namespace oodb.persistence
{
public class PersistenceCache
{
public OODB OODB { get; }
Dictionary<Type, TypeCache> typeCaches = new Dictionary<Type, TypeCache>();
public PersistenceCache(OODB oodb)
{
OODB = oodb;
}
private TypeCache GetTypeCache(Type type)
{
if (!typeCaches.ContainsKey(type))
typeCaches[type] = new TypeCache(this,type);
return typeCaches[type];
}
public Persistent Get(Type type,Guid persistenceID)
{
return GetTypeCache(type).Get(persistenceID);
}
public void Touch(Persistent persistent)
{
GetTypeCache(persistent.GetType()).Touch(persistent);
}
public void Remove(Persistent persistent)
{
GetTypeCache(persistent.GetType()).Remove(persistent);
}
class TypeCache
{
PersistenceCache PersistenceCache { get; }
TypeCache BaseCache { get; }
Type CacheType { get; }
Descriptor Descriptor { get; }
Dictionary<Guid, WeakReference> cache = new Dictionary<Guid, WeakReference>();
public TypeCache(PersistenceCache persistenceCache,Type type)
{
PersistenceCache = persistenceCache;
CacheType = type;
Descriptor = persistenceCache.OODB.GetDescriptor(type);
if (Descriptor.BaseDescriptor != null)
BaseCache = persistenceCache.GetTypeCache(Descriptor.BaseDescriptor.NativeType);
}
public Persistent Get(Guid persistenceID)
{
WeakReference weakReference = cache[persistenceID];
if (!weakReference.IsAlive)
{
Remove(persistenceID);
throw new KeyNotFoundException(persistenceID.ToString());
}
return weakReference.Target as Persistent;
}
public void Touch(Persistent persistent)
{
cache[persistent.PersistenceID] = new WeakReference(persistent);
if (Descriptor.BaseDescriptor != null)
PersistenceCache.GetTypeCache(Descriptor.BaseDescriptor.NativeType).Touch(persistent);
}
public void Remove(Persistent persistent)
{
Remove(persistent.PersistenceID);
}
public void Remove(Guid persistenceID)
{
cache.Remove(persistenceID);
if (Descriptor.BaseDescriptor != null)
{
PersistenceCache.GetTypeCache(Descriptor.BaseDescriptor.NativeType).Remove(persistenceID);
}
}
}
}
}

75
sharp-oodb.csproj 100644
View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{FF9FA8D2-5E89-4609-83F0-B9512DF777BE}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>oodb</RootNamespace>
<AssemblyName>sharp-oodb</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ExternalConsole>true</ExternalConsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ExternalConsole>true</ExternalConsole>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="OODB.cs" />
<Compile Include="mapping\FlatTypeMapping.cs" />
<Compile Include="Persistent.cs" />
<Compile Include="descriptor\Descriptor.cs" />
<Compile Include="mapping\ListMapping.cs" />
<Compile Include="mapping\StringMapping.cs" />
<Compile Include="mapping\IntegerMapping.cs" />
<Compile Include="mapping\DoubleMapping.cs" />
<Compile Include="mapping\ReferenceMapping.cs" />
<Compile Include="persistence\PersistenceCache.cs" />
<Compile Include="StorageAdapter.cs" />
<Compile Include="mapping\ArrayMapping.cs" />
<Compile Include="index\Index.cs" />
<Compile Include="index\Unique.cs" />
<Compile Include="attributes\UniqueAttribute.cs" />
<Compile Include="attributes\ReferencedFieldAttribute.cs" />
<Compile Include="mapping\collections\ISetImplementation.cs" />
<Compile Include="Query.cs" />
<Compile Include="index\SearchIndex.cs" />
<Compile Include="mapping\ExtendedFieldHandling.cs" />
<Compile Include="mapping\collections\ISetExtendedHandling.cs" />
<Compile Include="attributes\IndexedAttribute.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="mapping\" />
<Folder Include="descriptor\" />
<Folder Include="persistence\" />
<Folder Include="index\" />
<Folder Include="attributes\" />
<Folder Include="mapping\collections\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\sharp-logging\sharp.logging.csproj">
<Project>{D471A566-9FB6-41B2-A777-3C32874ECD0E}</Project>
<Name>sharp.logging</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>