using System; using System.Collections.Generic; using System.Diagnostics; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace Jovian.Utilities { /// /// Editor use-only.
/// Allows easy access between different instances. Supports 1 instance per type.
/// Add instances and retrieve elsewhere.

/// Read more: https://en.wikipedia.org/wiki/Service_locator_pattern ///
public static class EditorServiceLocator { private sealed class ServiceNotFoundException : Exception { public ServiceNotFoundException(Type type) : base($"ServiceNotFoundException. Type={type}") { } } private static Dictionary serviceContainer = new(); public static bool IsDirty { get; private set; } #if UNITY_EDITOR public static Dictionary ServiceContainer => serviceContainer; #endif [RuntimeInitializeOnLoadMethod] public static void Init() { serviceContainer = new(); } #if UNITY_EDITOR [InitializeOnEnterPlayMode] private static void Reset(EnterPlayModeOptions options) { serviceContainer?.Clear(); IsDirty = true; } #endif /// /// Add an instance to the locator. Will throw an exception if a type already exists in the locator. /// /// [Conditional("UNITY_EDITOR")] public static void Add(T service) { #if UNITY_EDITOR if (service == null) { throw new NullReferenceException($"Service is null. Expected instance of type '{typeof(T)}'"); } serviceContainer.Add(typeof(T), service); IsDirty = true; #else UnityEngine.Debug.LogWarning($"EditorServiceLocator should not be used outside of the Editor"); #endif } /// /// Add an instance to the locator. Will replace any existing instance without an exception. /// An alias for /// [Conditional("UNITY_EDITOR")] public static void AddOrReplace(T service) { Set(service); } /// /// Add an instance to the locator. Will replace any existing instance without an exception. /// An alias for /// [Conditional("UNITY_EDITOR")] public static void Set(T service) { #if UNITY_EDITOR if (service == null) { throw new NullReferenceException($"Service is null. Expected instance of type '{typeof(T)}'"); } serviceContainer[typeof(T)] = service; IsDirty = true; #else UnityEngine.Debug.LogWarning($"EditorServiceLocator should not be used outside of the Editor"); #endif } /// /// Removes any type matching the instance passed in from the locator. This is good practice. /// /// /// /// [Conditional("UNITY_EDITOR")] public static void Remove(T service) { #if UNITY_EDITOR if (service == null) { throw new NullReferenceException($"Service is null. Expected instance of type '{typeof(T)}'"); } serviceContainer.Remove(typeof(T)); IsDirty = true; #else UnityEngine.Debug.LogWarning($"EditorServiceLocator should not be used outside of the Editor"); #endif } /// /// Retrieves an instance from the locator matching the type. Will throw an exception if nothing is found. /// /// public static T Get() { #if UNITY_EDITOR if (serviceContainer.TryGetValue(typeof(T), out object service)) { return (T)service; } #endif throw new ServiceNotFoundException(typeof(T)); } [Obsolete("Use Get or TryGet(out T) since they follow the C# conventions for TryGet")] public static T TryGet() { #if UNITY_EDITOR if (serviceContainer.TryGetValue(typeof(T), out object service)) { return (T)service; } #endif return default; } /// /// Retrieves an instance from the locator matching the type. Returns true/false based on success. /// public static bool TryGet(out T instance) { #if UNITY_EDITOR if (serviceContainer.TryGetValue(typeof(T), out object service)) { instance = (T)service; return instance != null; } #endif instance = default; return false; } [Conditional("UNITY_EDITOR")] public static void Clear() { serviceContainer.Clear(); IsDirty = true; } [Conditional("UNITY_EDITOR")] public static void Clean() { IsDirty = false; } } }