// This code is part of the Fungus library (http://fungusgames.com) maintained by Chris Gregan (http://twitter.com/gofungus). // It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE) using UnityEngine; using System.Collections.Generic; using System.Linq; using System.Text; namespace Fungus { /// /// Replaces special tokens in a string with substituted values (typically variables or localisation strings). /// public class StringSubstituter { /// /// Interface for components that support substituting strings. /// public interface ISubstitutionHandler { /// /// Modifies a StringBuilder so that tokens are replaced by subsituted values. /// It's up to clients how to implement substitution but the convention looks like: /// "Hi {$VarName}" => "Hi John" where VarName == "John" /// True if the input was modified /// bool SubstituteStrings(StringBuilder input); } protected List substitutionHandlers = new List(); /// /// The StringBuilder instance used to substitute strings optimally. /// This property is public to support client code optimisations. /// protected StringBuilder stringBuilder; public StringBuilder _StringBuilder { get { return stringBuilder; } } private int recursionDepth; /// /// Constructor which caches all components in the scene that implement ISubstitutionHandler. /// Number of levels of recursively embedded keys to resolve. /// public StringSubstituter(int recursionDepth = 5) { CacheSubstitutionHandlers(); stringBuilder = new StringBuilder(1024); this.recursionDepth = recursionDepth; } /// /// Populates a cache of all components in the scene that implement ISubstitutionHandler. /// public virtual void CacheSubstitutionHandlers() { // Use reflection to find all components in the scene that implement ISubstitutionHandler var types = this.GetType().Assembly.GetTypes().Where(type => type.IsClass && !type.IsAbstract && typeof(ISubstitutionHandler).IsAssignableFrom(type)); substitutionHandlers.Clear(); foreach (System.Type t in types) { Object[] objects = GameObject.FindObjectsOfType(t); foreach (Object o in objects) { ISubstitutionHandler handler = o as ISubstitutionHandler; if (handler != null) { substitutionHandlers.Add(handler); } } } } /// /// Returns a new string that has been processed by all substitution handlers in the scene. /// public virtual string SubstituteStrings(string input) { stringBuilder.Length = 0; stringBuilder.Append(input); if (SubstituteStrings(stringBuilder)) { return stringBuilder.ToString(); } else { return input; // String wasn't modified } } public virtual bool SubstituteStrings(StringBuilder input) { bool result = false; // Perform the substitution multiple times to expand nested keys int loopCount = 0; // Avoid infinite recursion loops while (loopCount < recursionDepth) { bool modified = false; foreach (ISubstitutionHandler handler in substitutionHandlers) { if (handler.SubstituteStrings(input)) { modified = true; result = true; } } if (!modified) { break; } loopCount++; } return result; } } }