//----------------------------------------------------------------------- // // Copyright (c) 2018 Sirenix IVS // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //----------------------------------------------------------------------- namespace VRC.Udon.Serialization.OdinSerializer { using System.Collections.Generic; using Utilities; /// /// Resolves external index references to Unity objects. /// /// /// public sealed class UnityReferenceResolver : IExternalIndexReferenceResolver, ICacheNotificationReceiver { private Dictionary referenceIndexMapping = new Dictionary(32, ReferenceEqualityComparer.Default); private List referencedUnityObjects; /// /// Initializes a new instance of the class. /// public UnityReferenceResolver() { this.referencedUnityObjects = new List(); } /// /// Initializes a new instance of the class with a list of Unity objects. /// /// The referenced Unity objects. public UnityReferenceResolver(List referencedUnityObjects) { this.SetReferencedUnityObjects(referencedUnityObjects); } /// /// Gets the currently referenced Unity objects. /// /// A list of the currently referenced Unity objects. public List GetReferencedUnityObjects() { return this.referencedUnityObjects; } /// /// Sets the referenced Unity objects of the resolver to a given list, or a new list if the value is null. /// /// The referenced Unity objects to set, or null if a new list is required. public void SetReferencedUnityObjects(List referencedUnityObjects) { if (referencedUnityObjects == null) { referencedUnityObjects = new List(); } this.referencedUnityObjects = referencedUnityObjects; this.referenceIndexMapping.Clear(); for (int i = 0; i < this.referencedUnityObjects.Count; i++) { if (object.ReferenceEquals(this.referencedUnityObjects[i], null) == false) { if (!this.referenceIndexMapping.ContainsKey(this.referencedUnityObjects[i])) { this.referenceIndexMapping.Add(this.referencedUnityObjects[i], i); } } } } /// /// Determines whether the specified value can be referenced externally via this resolver. /// /// The value to reference. /// The index of the resolved value, if it can be referenced. /// /// true if the reference can be resolved, otherwise false. /// public bool CanReference(object value, out int index) { if (this.referencedUnityObjects == null) { this.referencedUnityObjects = new List(32); } var obj = value as UnityEngine.Object; if (object.ReferenceEquals(null, obj) == false) { if (this.referenceIndexMapping.TryGetValue(obj, out index) == false) { index = this.referencedUnityObjects.Count; this.referenceIndexMapping.Add(obj, index); this.referencedUnityObjects.Add(obj); } return true; } index = -1; return false; } /// /// Tries to resolve the given reference index to a reference value. /// /// The index to resolve. /// The resolved value. /// /// true if the index could be resolved to a value, otherwise false. /// public bool TryResolveReference(int index, out object value) { if (this.referencedUnityObjects == null || index < 0 || index >= this.referencedUnityObjects.Count) { // Sometimes something has destroyed the list of references in between serialization and deserialization // (Unity prefab instances are especially bad at preserving such data), and in these cases we still don't // want the system to fall back to a formatter, so we give out a null value. value = null; return true; } value = this.referencedUnityObjects[index]; return true; } /// /// Resets this instance. /// public void Reset() { this.referencedUnityObjects = null; this.referenceIndexMapping.Clear(); } void ICacheNotificationReceiver.OnFreed() { this.Reset(); } void ICacheNotificationReceiver.OnClaimed() { } } }