Skip to content

Commit 12ed65c

Browse files
committed
Add initial support for the InteractableType wrapper.
1 parent 3aa8de6 commit 12ed65c

File tree

5 files changed

+268
-0
lines changed

5 files changed

+268
-0
lines changed

Assets/MixedRealityToolkit.SDK/Features/UX/Interactable/Scripts/TypeResolution.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Microsoft.MixedReality.Toolkit.Core.Definitions.Utilities;
2+
using System;
3+
using System.Collections.Generic;
4+
5+
namespace Microsoft.MixedReality.Toolkit.SDK.UX.Interactable
6+
{
7+
/// <summary>
8+
/// A wrapper for a Type which gives a "friendly name" for the type (i.e.
9+
/// the class name) along with the assembly qualified name (which can be used
10+
/// to new instances of this type).
11+
/// </summary>
12+
/// <remarks>
13+
/// The intent of this wrapper is for use with the various Interactable state, event
14+
/// and theme classes, which are enumerated using reflection in the editor but must
15+
/// then be instantiated at runtime (without the usage of reflection due to .NET
16+
/// backend constraints).
17+
/// </remarks>
18+
public class InteractableType
19+
{
20+
/// <summary>
21+
/// The class name of this interactable type (for example, "InteractableActivateTheme").
22+
/// </summary>
23+
public string ClassName { get; private set; }
24+
25+
/// <summary>
26+
/// The assembly qualified name of the class (for example,
27+
/// "Microsoft.MixedReality.Toolkit.SDK.UX.Interactable.Themes.InteractableActivateTheme,
28+
/// Microsoft.MixedReality.Toolkit.SDK")
29+
/// </summary>
30+
public string AssemblyQualifiedName { get; private set; }
31+
32+
/// <summary>
33+
/// The type of the class (for example, typeof(InteractableActivateTheme)).
34+
/// </summary>
35+
public Type Type { get; private set; }
36+
37+
public InteractableType(Type type)
38+
{
39+
ClassName = type.Name;
40+
AssemblyQualifiedName = SystemType.GetReference(type);
41+
Type = type;
42+
}
43+
}
44+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
5+
namespace Microsoft.MixedReality.Toolkit.SDK.UX.Interactable.TypeResolution
6+
{
7+
/// <summary>
8+
/// A helper that uses reflection to find objects that implement base types of the
9+
/// Interactable types that populate the various state, theme, and event inspectors.
10+
/// </summary>
11+
/// <remarks>
12+
///
13+
/// </remarks>
14+
public class InteractableTypeFinder
15+
{
16+
/// <summary>
17+
/// Controls the behavior of the InteractableTypeFinder.FindTypes function. See individual
18+
/// enum values for more details.
19+
/// </summary>
20+
public enum TypeRestriction
21+
{
22+
/// <summary>
23+
/// When this is specified, only classes derived from the specified type will be
24+
/// returned by the lookup. This means that if you pass InteractableStates, the
25+
/// lookup will only return classes whose base class is InteractableStates but
26+
/// will not return InteractableStates itself.
27+
/// </summary>
28+
DerivedOnly,
29+
30+
/// <summary>
31+
/// When this is specified, classes derived from the specified type AND the class
32+
/// itself will be returned by the lookup. This means that if you pass
33+
/// InteractableStates, the lookup will both classes whose base class is
34+
/// InteractableStates and InteractableStates itself.
35+
/// </summary>
36+
AllowBase,
37+
};
38+
39+
/// <summary>
40+
/// A convenience wrapper provided for editor code to turn a list of types into a form that
41+
/// matches their existing structure.
42+
/// </summary>
43+
/// <remarks>
44+
/// This is primarily a crutch because of how the inspector code stores parallel arrays of
45+
/// objects, rather than just storing an array of objects (i.e. it stores three arrays
46+
/// of objects which happen to have matching indices, rather than storing a single array
47+
/// of objects which have state relevant within the object).
48+
/// </remarks>
49+
public static InteractableTypesContainer Find(List<Type> types, TypeRestriction typeRestriction)
50+
{
51+
#if UNITY_EDITOR
52+
return new InteractableTypesContainer(FindTypes(types, typeRestriction));
53+
#else
54+
// Due to other code structure, it's possible that this can still be invoked at runtime, but should
55+
// not return anything (because type information should be read from serialized assembly data, rather
56+
// than using reflection at runtime).
57+
return new InteractableTypeList(new List<InteractableType>());
58+
#endif
59+
}
60+
61+
#if UNITY_EDITOR
62+
/// <summary>
63+
/// Used to cache lookups for Types (for example, InteractableThemeBase) to their classes that implement
64+
/// that type.
65+
/// </summary>
66+
private static Dictionary<Type, List<InteractableType>> cache = new Dictionary<Type, List<InteractableType>>();
67+
68+
/// <summary>
69+
/// Gets the list of InteractableType objects for classes that support the specified types.
70+
/// </summary>
71+
private static List<InteractableType> FindTypes(List<Type> types, TypeRestriction typeRestriction)
72+
{
73+
EnsureCacheForTypes(types, typeRestriction);
74+
return GetTypesFromCache(types);
75+
}
76+
77+
/// <summary>
78+
/// Gets the list of InteractableType objects for classes that support the specified types by
79+
/// looking directly in the cache.
80+
/// </summary>
81+
/// <remarks>
82+
/// Assumes it is called after EnsureCacheForTypes. Otherwise, this is dangerous to call.
83+
/// </remarks>
84+
private static List<InteractableType> GetTypesFromCache(List<Type> types)
85+
{
86+
List<InteractableType> interactableTypes = new List<InteractableType>();
87+
foreach (Type type in types)
88+
{
89+
interactableTypes.AddRange(cache[type]);
90+
}
91+
return interactableTypes;
92+
}
93+
94+
/// <summary>
95+
/// Ensures a cache entry is setup for all types in the InteractableType enum.
96+
/// </summary>
97+
/// <remarks>
98+
/// Note that this is not invoked at runtime and is assumed to be invoked from a single
99+
/// threaded UI context, and is thus not locked.
100+
/// </remarks>
101+
private static void EnsureCacheForTypes(List<Type> types, TypeRestriction typeRestriction)
102+
{
103+
List<Type> cacheMisses = new List<Type>();
104+
foreach (Type type in types)
105+
{
106+
if (!cache.ContainsKey(type))
107+
{
108+
cacheMisses.Add(type);
109+
}
110+
}
111+
112+
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
113+
foreach (Type type in cacheMisses)
114+
{
115+
cache[type] = GetTypesFromAssemblies(type, typeRestriction, assemblies);
116+
}
117+
}
118+
119+
/// <summary>
120+
/// Loads the classes that derive from the given type by looking through all of the assemblies.
121+
/// </summary>
122+
private static List<InteractableType> GetTypesFromAssemblies(Type type, TypeRestriction typeRestriction, Assembly[] assemblies)
123+
{
124+
List<InteractableType> interactableTypes = new List<InteractableType>();
125+
foreach (Assembly assembly in assemblies)
126+
{
127+
foreach (Type assemblyType in assembly.GetTypes())
128+
{
129+
TypeInfo info = assemblyType.GetTypeInfo();
130+
bool exactBaseMatch = typeRestriction == TypeRestriction.AllowBase && assemblyType.Equals(type);
131+
bool derivedMatch = info.BaseType != null && info.BaseType.Equals(type);
132+
if (exactBaseMatch || derivedMatch)
133+
{
134+
InteractableType interactableType = new InteractableType(assemblyType);
135+
interactableTypes.Add(interactableType);
136+
}
137+
}
138+
}
139+
return interactableTypes;
140+
}
141+
#endif
142+
}
143+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
5+
namespace Microsoft.MixedReality.Toolkit.SDK.UX.Interactable
6+
{
7+
/// <summary>
8+
/// A convenience class that holds arrays of class names, fully qualified assembly names
9+
/// and their corresponding actual types.
10+
/// </summary>
11+
/// <remarks>
12+
/// This abstraction exists primarily to reduce code duplication among the different
13+
/// inspectors which use these lists to populate their dropdowns.
14+
///
15+
/// Note that all of these arrays are the same size and come in the same order
16+
/// (so for example, ClassName[0] = "InteractableActivateTheme" means that
17+
/// Types[0] == typeof(InteractableActivateTheme) and AssemblyQualifiedNames
18+
/// is the assembly qualified name for InteractableActivateTheme.
19+
/// </remarks>
20+
public class InteractableTypesContainer
21+
{
22+
/// <summary>
23+
/// An array of class names (for example, "InteractableActivateTheme").
24+
/// </summary>
25+
public string[] ClassNames { get; private set; }
26+
27+
/// <summary>
28+
/// A array of assembly qualified names (for example,
29+
/// "Microsoft.MixedReality.Toolkit.SDK.UX.Interactable.Themes.InteractableActivateTheme,
30+
/// Microsoft.MixedReality.Toolkit.SDK")
31+
/// </summary>
32+
public string[] AssemblyQualifiedNames { get; private set; }
33+
34+
/// <summary>
35+
/// An array of types. See class remarks for more information on relation to
36+
/// other fields.
37+
/// </summary>
38+
public Type[] Types { get; private set; }
39+
40+
/// <summary>
41+
/// A convenience helper that will unwrap a list of InteractableType objects into
42+
/// a form that is more easy consumed by inspector components.
43+
/// </summary>
44+
public InteractableTypesContainer(List<InteractableType> interactableTypes)
45+
{
46+
var classNames = new List<string>();
47+
var assemblyQualifiedNames = new List<string>();
48+
var types = new List<Type>();
49+
50+
for (int i = 0; i < interactableTypes.Count; i++)
51+
{
52+
classNames.Add(interactableTypes[i].ClassName);
53+
assemblyQualifiedNames.Add(interactableTypes[i].AssemblyQualifiedName);
54+
types.Add(interactableTypes[i].Type);
55+
}
56+
57+
ClassNames = classNames.ToArray();
58+
AssemblyQualifiedNames = assemblyQualifiedNames.ToArray();
59+
Types = types.ToArray();
60+
}
61+
}
62+
}

Assets/MixedRealityToolkit.SDK/Features/UX/Interactable/Scripts/TypeResolution/InteractableTypesContainer.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)