Skip to content

Instantly share code, notes, and snippets.

@pmunin
Created April 25, 2016 15:27
Show Gist options
  • Save pmunin/df68a562db7b580627f1d6859ff189d5 to your computer and use it in GitHub Desktop.
Save pmunin/df68a562db7b580627f1d6859ff189d5 to your computer and use it in GitHub Desktop.
Allows to create annotation (dynamically linked objects/wrappers)
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace ObjectAnnotation
{
public static class ObjectAnnotationExtensions
{
//TODO: Make thread safe
static ConditionalWeakTable<object, List<object>> annotationsBySource
= new ConditionalWeakTable<object, List<object>>();
static ConditionalWeakTable<object, object> sourceByAnnotation
= new ConditionalWeakTable<object, object>();
static List<object> AnnotationsBySource(object source)
{
return annotationsBySource.GetOrCreateValue(source);
}
public static TAnnotation Annotation<TAnnotation>(this object source)
{
return source.Annotation<object, TAnnotation>(null);
}
public static TAnnotation Annotation<TAnnotation>(this object source
, Func<object, TAnnotation> createIfNotExists)
{
return source.Annotation<object, TAnnotation>(createIfNotExists);
}
public static TAnnotation Annotation<TObject, TAnnotation>(this TObject source
, Func<TObject, TAnnotation> createIfNotExists)
{
var wrappers = AnnotationsBySource(source);
var wrapper = wrappers.OfType<TAnnotation>().FirstOrDefault();
if (object.Equals(wrapper,default(TAnnotation)) && createIfNotExists != null)
{
wrapper = createIfNotExists(source);
if (!object.Equals(wrapper,default(TAnnotation)))
wrappers.Add(wrapper);
sourceByAnnotation.Remove(wrapper);
sourceByAnnotation.Add(wrapper, source);
}
return wrapper;
}
public static void Annotation<TAnnotation>(this object source
, TAnnotation newWrapper, bool removeOthers = true)
{
var wrappers = AnnotationsBySource(source);
if (removeOthers)
{
var wrappersToRemove = wrappers.OfType<TAnnotation>().ToArray();
wrappersToRemove.ForEach(w => source.AnnotationRemove(w));
}
if(!wrappers.Contains(newWrapper))
wrappers.Add(newWrapper);
}
static void AnnotationRemove(this object source, object annotation)
{
var wrappers = AnnotationsBySource(source);
wrappers.Remove(annotation);
sourceByAnnotation.Remove(annotation);
}
public static IEnumerable<TAnnotation> Annotations<TAnnotation>(this object source)
{
var wrappers = annotationsBySource.GetOrCreateValue(source);
return wrappers.OfType<TAnnotation>();
}
public static TTarget AnnotationTarget<TTarget>(this object annotation)
{
return (TTarget)annotation.AnnotationTarget();
}
public static object AnnotationTarget(this object annotation)
{
object res;
if (sourceByAnnotation.TryGetValue(annotation, out res))
return res;
return null;
}
public static void AnnotationTarget<TAnnotation>(this TAnnotation annotation, object newSource)
{
var oldSource = annotation.AnnotationTarget();
if (oldSource != null)
oldSource.AnnotationRemove(annotation);
newSource.Annotation(annotation, true);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment