csharp-sdk-upm/Storage/Source/Internal/Object/Subclassing/ObjectSubclassingController.cs

172 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using LeanCloud.Storage.Internal;
namespace LeanCloud.Storage.Internal
{
internal class ObjectSubclassingController : IObjectSubclassingController
{
// Class names starting with _ are documented to be reserved. Use this one
// here to allow us to 'inherit' certain properties.
private static readonly string avObjectClassName = "_AVObject";
private readonly ReaderWriterLockSlim mutex;
private readonly IDictionary<String, ObjectSubclassInfo> registeredSubclasses;
private Dictionary<String, Action> registerActions;
public ObjectSubclassingController()
{
mutex = new ReaderWriterLockSlim();
registeredSubclasses = new Dictionary<String, ObjectSubclassInfo>();
registerActions = new Dictionary<string, Action>();
// Register the AVObject subclass, so we get access to the ACL,
// objectId, and other AVFieldName properties.
RegisterSubclass(typeof(AVObject));
}
public String GetClassName(Type type)
{
return type == typeof(AVObject)
? avObjectClassName
: ObjectSubclassInfo.GetClassName(type.GetTypeInfo());
}
public Type GetType(String className)
{
ObjectSubclassInfo info = null;
mutex.EnterReadLock();
registeredSubclasses.TryGetValue(className, out info);
mutex.ExitReadLock();
return info != null
? info.TypeInfo.AsType()
: null;
}
public bool IsTypeValid(String className, Type type)
{
ObjectSubclassInfo subclassInfo = null;
mutex.EnterReadLock();
registeredSubclasses.TryGetValue(className, out subclassInfo);
mutex.ExitReadLock();
return subclassInfo == null
? type == typeof(AVObject)
: subclassInfo.TypeInfo == type.GetTypeInfo();
}
public void RegisterSubclass(Type type)
{
TypeInfo typeInfo = type.GetTypeInfo();
if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(typeInfo))
{
throw new ArgumentException("Cannot register a type that is not a subclass of AVObject");
}
String className = GetClassName(type);
try
{
// Perform this as a single independent transaction, so we can never get into an
// intermediate state where we *theoretically* register the wrong class due to a
// TOCTTOU bug.
mutex.EnterWriteLock();
ObjectSubclassInfo previousInfo = null;
if (registeredSubclasses.TryGetValue(className, out previousInfo))
{
if (typeInfo.IsAssignableFrom(previousInfo.TypeInfo))
{
// Previous subclass is more specific or equal to the current type, do nothing.
return;
}
else if (previousInfo.TypeInfo.IsAssignableFrom(typeInfo))
{
// Previous subclass is parent of new child, fallthrough and actually register
// this class.
/* Do nothing */
}
else
{
throw new ArgumentException(
"Tried to register both " + previousInfo.TypeInfo.FullName + " and " + typeInfo.FullName +
" as the AVObject subclass of " + className + ". Cannot determine the right class " +
"to use because neither inherits from the other."
);
}
}
ConstructorInfo constructor = type.FindConstructor();
if (constructor == null)
{
throw new ArgumentException("Cannot register a type that does not implement the default constructor!");
}
registeredSubclasses[className] = new ObjectSubclassInfo(type, constructor);
}
finally
{
mutex.ExitWriteLock();
}
Action toPerform;
mutex.EnterReadLock();
registerActions.TryGetValue(className, out toPerform);
mutex.ExitReadLock();
if (toPerform != null)
{
toPerform();
}
}
public void UnregisterSubclass(Type type)
{
mutex.EnterWriteLock();
registeredSubclasses.Remove(GetClassName(type));
mutex.ExitWriteLock();
}
public void AddRegisterHook(Type t, Action action)
{
mutex.EnterWriteLock();
registerActions.Add(GetClassName(t), action);
mutex.ExitWriteLock();
}
public AVObject Instantiate(String className)
{
ObjectSubclassInfo info = null;
mutex.EnterReadLock();
registeredSubclasses.TryGetValue(className, out info);
mutex.ExitReadLock();
return info != null
? info.Instantiate()
: new AVObject(className);
}
public IDictionary<String, String> GetPropertyMappings(String className)
{
ObjectSubclassInfo info = null;
mutex.EnterReadLock();
registeredSubclasses.TryGetValue(className, out info);
if (info == null)
{
registeredSubclasses.TryGetValue(avObjectClassName, out info);
}
mutex.ExitReadLock();
return info.PropertyMappings;
}
}
}