-
-
Notifications
You must be signed in to change notification settings - Fork 261
/
Copy pathParseObjectClassController.cs
146 lines (113 loc) · 5.48 KB
/
ParseObjectClassController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using Parse.Abstractions.Infrastructure;
using Parse.Abstractions.Platform.Objects;
using Parse.Infrastructure.Utilities;
namespace Parse.Platform.Objects
{
internal class ParseObjectClassController : IParseObjectClassController
{
// Class names starting with _ are documented to be reserved. Use this one here to allow us to "inherit" certain properties.
static string ReservedParseObjectClassName { get; } = "_ParseObject";
ReaderWriterLockSlim Mutex { get; } = new ReaderWriterLockSlim { };
IDictionary<string, ParseObjectClass> Classes { get; } = new Dictionary<string, ParseObjectClass> { };
Dictionary<string, Action> RegisterActions { get; set; } = new Dictionary<string, Action> { };
public ParseObjectClassController() => AddValid(typeof(ParseObject));
public string GetClassName(Type type) => type == typeof(ParseObject) ? ReservedParseObjectClassName : type.GetParseClassName();
public Type GetType(string className)
{
Mutex.EnterReadLock();
Classes.TryGetValue(className, out ParseObjectClass info);
Mutex.ExitReadLock();
return info?.TypeInfo.AsType();
}
public bool GetClassMatch(string className, Type type)
{
Mutex.EnterReadLock();
Classes.TryGetValue(className, out ParseObjectClass subclassInfo);
Mutex.ExitReadLock();
return subclassInfo is { } ? subclassInfo.TypeInfo == type.GetTypeInfo() : type == typeof(ParseObject);
}
public void AddValid(Type type)
{
TypeInfo typeInfo = type.GetTypeInfo();
if (!typeof(ParseObject).GetTypeInfo().IsAssignableFrom(typeInfo))
throw new ArgumentException("Cannot register a type that is not a subclass of ParseObject");
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();
if (Classes.TryGetValue(className, out ParseObjectClass 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 ParseObject subclass of {className}. Cannot determine the right class to use because neither inherits from the other.");
#warning Constructor detection may erroneously find a constructor which should not be used.
ConstructorInfo constructor = type.FindConstructor() ?? type.FindConstructor(typeof(string), typeof(IServiceHub));
if (constructor is null)
throw new ArgumentException("Cannot register a type that does not implement the default constructor!");
Classes[className] = new ParseObjectClass(type, constructor);
}
finally
{
Mutex.ExitWriteLock();
}
Mutex.EnterReadLock();
RegisterActions.TryGetValue(className, out Action toPerform);
Mutex.ExitReadLock();
toPerform?.Invoke();
}
public void RemoveClass(Type type)
{
Mutex.EnterWriteLock();
Classes.Remove(GetClassName(type));
Mutex.ExitWriteLock();
}
public void AddRegisterHook(Type type, Action action)
{
Mutex.EnterWriteLock();
RegisterActions.Add(GetClassName(type), action);
Mutex.ExitWriteLock();
}
public ParseObject Instantiate(string className, IServiceHub serviceHub)
{
Mutex.EnterReadLock();
Classes.TryGetValue(className, out ParseObjectClass info);
Mutex.ExitReadLock();
serviceHub = serviceHub ?? ParseClient.Instance;
return info is { } ? info.Instantiate().Bind(serviceHub) : new ParseObject(className, serviceHub);
}
public IDictionary<string, string> GetPropertyMappings(string className)
{
Mutex.EnterReadLock();
Classes.TryGetValue(className, out ParseObjectClass info);
if (info is null)
Classes.TryGetValue(ReservedParseObjectClassName, out info);
Mutex.ExitReadLock();
return info.PropertyMappings;
}
bool SDKClassesAdded { get; set; }
// ALTERNATE NAME: AddObject, AddType, AcknowledgeType, CatalogType
public void AddIntrinsic()
{
if (!(SDKClassesAdded, SDKClassesAdded = true).SDKClassesAdded)
{
AddValid(typeof(ParseUser));
AddValid(typeof(ParseRole));
AddValid(typeof(ParseSession));
AddValid(typeof(ParseInstallation));
}
}
}
}