Tanoda
ImplementsInterface.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using UnityEngine;
10using System;
11using System.Collections.Generic;
12using Leap.Unity.Query;
13using System.Text.RegularExpressions;
14
15#if UNITY_EDITOR
16using UnityEditor;
17#endif
18
19using UnityObject = UnityEngine.Object;
20
21namespace Leap.Unity.Attributes {
22
27
28#pragma warning disable 0414
29 private Type type;
30#pragma warning restore 0414
31
32 public ImplementsInterfaceAttribute(Type type) {
33 if (!type.IsInterface) {
34 throw new System.Exception(type.Name + " is not an interface.");
35 }
36 this.type = type;
37 }
38
39#if UNITY_EDITOR
40 public void ConstrainValue(SerializedProperty property) {
41 if (property.objectReferenceValue != null) {
42
43 UnityObject implementingObject = FindImplementer(property,
44 property.objectReferenceValue);
45
46 if (implementingObject == null) {
47 Debug.LogError(property.objectReferenceValue.GetType().Name + " does not implement " + type.Name);
48 }
49
50 property.objectReferenceValue = implementingObject;
51 }
52 }
53
59 public UnityObject FindImplementer(SerializedProperty property, UnityObject fromObj) {
60
61 // Don't use fieldInfo here because it is very convenient for it to be left null
62 // on the CombinablePropertyAttribute when dealing with generic-type situations
63 // where the ImplementsInterface class has to be constructed manually in a custom
64 // editor. (Specific case: StreamConnectorEditor.cs)
65
66 bool isTypeDefinitelyIncompatible;
67 {
68 isTypeDefinitelyIncompatible = !this.type.IsAssignableFrom(fromObj.GetType());
69
70 // We have to make an exception when a GameObject is dragged into a field whose
71 // type is a Component, because it's expected that we can use GetComponent in
72 // that case. (We might still fail later if the component isn't found.)
73 var objIsGameObject = fromObj.GetType() == typeof(GameObject);
74 if (objIsGameObject) {
75 isTypeDefinitelyIncompatible = false;
76 }
77
78 // We also make an exception when a Component is dragged into a field that
79 // doesn't directly satisfy the interface because it might have other Components
80 // on its GameObject that _do_ satisfy the field, again via GetComponent.
81 var objIsComponent = typeof(Component).IsAssignableFrom(fromObj.GetType());
82 if (objIsComponent) {
83 isTypeDefinitelyIncompatible = false;
84 }
85
86 // However, you can't assign a ScriptableObject to a field expecting a Component,
87 // or vice-versa, no matter what.
88
89 var fieldIsScriptableObject
90 = fieldInfo.FieldType.IsAssignableFrom(typeof(ScriptableObject));
91 if (fieldIsScriptableObject && (objIsComponent || objIsGameObject)) {
92 isTypeDefinitelyIncompatible = true;
93 }
94
95 var fieldTakesComponent
96 = typeof(Component).IsAssignableFrom(fieldInfo.FieldType);
97 if (fieldTakesComponent && (!objIsComponent && !objIsGameObject)) {
98 isTypeDefinitelyIncompatible = true;
99 }
100 }
101 if (isTypeDefinitelyIncompatible) {
102 return null;
103 }
104
105 if (fromObj.GetType().ImplementsInterface(type)) {
106 // All good! This object reference implements the interface.
107 return fromObj;
108 }
109 else {
110 UnityObject implementingObject;
111
112 if (fromObj is GameObject) {
113 fromObj = (fromObj as GameObject).transform;
114 }
115
116 if (fromObj is Component) {
117 // If the object is a Component, first search the rest of the GameObject
118 // for a component that implements the interface. If found, assign it instead,
119 // otherwise null out the property.
120 implementingObject = (fromObj as Component)
121 .GetComponents<Component>()
122 .Query()
123 .Where(c => c.GetType().ImplementsInterface(type))
124 .FirstOrDefault();
125 }
126 else {
127 // If the object is not a Component, just null out the property.
128 implementingObject = null;
129 }
130
131 return implementingObject;
132 }
133 }
134
135 public void DrawProperty(Rect rect, SerializedProperty property, GUIContent label) {
136 if (property.objectReferenceValue != null) {
137 EditorGUI.ObjectField(rect, property, type, label);
138 }
139 else {
140 EditorGUI.ObjectField(rect, label, null, type, false);
141 }
142 }
143
144 public Rect GetDropArea(Rect rect, SerializedProperty property) {
145 return rect;
146 }
147
148 public bool IsDropValid(UnityObject[] draggedObjects, SerializedProperty property) {
149 return draggedObjects.Query().Any(o => FindImplementer(property, o) != null);
150 }
151
152 public void ProcessDroppedObjects(UnityObject[] droppedObjects,
153 SerializedProperty property) {
154
155 var implementer = droppedObjects.Query()
156 .FirstOrDefault(o => FindImplementer(property, o));
157
158 if (implementer == null) {
159 Debug.LogError(property.objectReferenceValue.GetType().Name
160 + " does not implement " + type.Name);
161 }
162 else {
163 property.objectReferenceValue = implementer;
164 }
165 }
166
167 public override IEnumerable<SerializedPropertyType> SupportedTypes {
168 get {
169 yield return SerializedPropertyType.ObjectReference;
170 }
171 }
172#endif
173 }
174}
UnityEngine.Object UnityObject
UnityEngine.Component Component
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19