Firstborn/Library/PackageCache/com.unity.addressables@1.19.19/Runtime/Initialization/AddressablesRuntimeProperti...

222 lines
8.3 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEngine.AddressableAssets.Initialization
{
/// <summary>
/// Supports the evaluation of embedded runtime variables in addressables locations
/// </summary>
public static class AddressablesRuntimeProperties
{
// cache these to avoid GC allocations
static Stack<string> s_TokenStack = new Stack<string>(32);
static Stack<int> s_TokenStartStack = new Stack<int>(32);
static bool s_StaticStacksAreInUse = false;
#if !UNITY_EDITOR && UNITY_WSA_10_0 && ENABLE_DOTNET
static Assembly[] GetAssemblies()
{
//Not supported on UWP platforms
return new Assembly[0];
}
#else
static Assembly[] GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
#endif
static Dictionary<string, string> s_CachedValues = new Dictionary<string, string>();
internal static int GetCachedValueCount()
{
return s_CachedValues.Count;
}
/// <summary>
/// Predefine a runtime property.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="val">The property value.</param>
public static void SetPropertyValue(string name, string val)
{
s_CachedValues[name] = val;
}
/// <summary>
/// This will clear all PropertyValues that have been cached. This includes all values set by
/// <see cref="SetPropertyValue"/> as well as any reflection-evaluated properties.
/// </summary>
public static void ClearCachedPropertyValues()
{
s_CachedValues.Clear();
}
/// <summary>
/// Evaluates a named property using cached values and static public fields and properties. Be aware that a field or property may be stripped if not referenced anywhere else.
/// </summary>
/// <param name="name">The property name.</param>
/// <returns>The value of the property. If not found, the name is returned.</returns>
public static string EvaluateProperty(string name)
{
Debug.Assert(s_CachedValues != null, "ResourceManagerConfig.GetGlobalVar - s_cachedValues == null.");
if (string.IsNullOrEmpty(name))
return string.Empty;
string cachedValue;
if (s_CachedValues.TryGetValue(name, out cachedValue))
return cachedValue;
int i = name.LastIndexOf('.');
if (i < 0)
return name;
var className = name.Substring(0, i);
var propName = name.Substring(i + 1);
foreach (var a in GetAssemblies())
{
Type t = a.GetType(className, false, false);
if (t == null)
continue;
try
{
var pi = t.GetProperty(propName, BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public);
if (pi != null)
{
var v = pi.GetValue(null, null);
if (v != null)
{
s_CachedValues.Add(name, v.ToString());
return v.ToString();
}
}
var fi = t.GetField(propName, BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public);
if (fi != null)
{
var v = fi.GetValue(null);
if (v != null)
{
s_CachedValues.Add(name, v.ToString());
return v.ToString();
}
}
}
catch (Exception)
{
// ignored
}
}
return name;
}
/// <summary>
/// Evaluates all tokens deliminated by '{' and '}' in a string and evaluates them with the EvaluateProperty method.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The evaluated string after resolving all tokens.</returns>
public static string EvaluateString(string input)
{
return EvaluateString(input, '{', '}', EvaluateProperty);
}
/// <summary>
/// Evaluates all tokens deliminated by the specified delimiters in a string and evaluates them with the supplied method.
/// </summary>
/// <param name="inputString">The string to evaluate.</param>
/// <param name="startDelimiter">The start token delimiter.</param>
/// <param name="endDelimiter">The end token delimiter.</param>
/// <param name="varFunc">Func that has a single string parameter and returns a string.</param>
/// <returns>The evaluated string.</returns>
public static string EvaluateString(string inputString, char startDelimiter, char endDelimiter, Func<string, string> varFunc)
{
if (string.IsNullOrEmpty(inputString))
return string.Empty;
string originalString = inputString;
Stack<string> tokenStack;
Stack<int> tokenStartStack;
if (!s_StaticStacksAreInUse)
{
tokenStack = s_TokenStack;
tokenStartStack = s_TokenStartStack;
s_StaticStacksAreInUse = true;
}
else
{
tokenStack = new Stack<string>(32);
tokenStartStack = new Stack<int>(32);
}
tokenStack.Push(inputString);
int popTokenAt = inputString.Length;
char[] delimiters = {startDelimiter, endDelimiter};
bool delimitersMatch = startDelimiter == endDelimiter;
int i = inputString.IndexOf(startDelimiter);
int prevIndex = -2;
while (i >= 0)
{
char c = inputString[i];
if (c == startDelimiter && (!delimitersMatch || tokenStartStack.Count == 0))
{
tokenStartStack.Push(i);
i++;
}
else if (c == endDelimiter && tokenStartStack.Count > 0)
{
int start = tokenStartStack.Peek();
string token = inputString.Substring(start + 1, i - start - 1);
string tokenVal;
if (popTokenAt <= i)
{
tokenStack.Pop();
}
// check if the token is already included
if (tokenStack.Contains(token))
tokenVal = "#ERROR-CyclicToken#";
else
{
tokenVal = varFunc == null ? string.Empty : varFunc(token);
tokenStack.Push(token);
}
i = tokenStartStack.Pop();
popTokenAt = i + tokenVal.Length + 1;
if (i > 0)
{
int rhsStartIndex = i + token.Length + 2;
if (rhsStartIndex == inputString.Length)
inputString = inputString.Substring(0, i) + tokenVal;
else
inputString = inputString.Substring(0, i) + tokenVal + inputString.Substring(rhsStartIndex);
}
else
inputString = tokenVal + inputString.Substring(i + token.Length + 2);
}
bool infiniteLoopDetected = prevIndex == i;
if (infiniteLoopDetected)
return "#ERROR-" + originalString +" contains unmatched delimiters#";
prevIndex = i;
i = inputString.IndexOfAny(delimiters, i);
}
tokenStack.Clear();
tokenStartStack.Clear();
if (ReferenceEquals(tokenStack, s_TokenStack))
s_StaticStacksAreInUse = false;
return inputString;
}
}
}