/********* ** Private methods *********/ /// <summary>Force a context update.</summary> /// <param name="context">Provides access to contextual tokens.</param> /// <param name="forceUpdate">Update even if the state hasn't changed.</param> /// <returns>Returns whether the context changed.</returns> private bool UpdateContext(IContext context, bool forceUpdate) { if (!forceUpdate && (!this.IsMutable || this.State.InvalidTokens.Any() || this.State.UnavailableModTokens.Any())) { return(false); } // reset string wasValue = this.Value; bool wasReady = this.State.IsReady; this.State.Reset(); // update value InvariantHashSet unavailableTokens = new InvariantHashSet(); InvariantHashSet errors = new InvariantHashSet(); { StringBuilder str = new StringBuilder(); foreach (TokenStringPart part in this.Parts) { str.Append(this.TryGetTokenText(context, part, unavailableTokens, errors, out string text) ? text : part.LexToken.ToString()); } this.Value = !unavailableTokens.Any() && !errors.Any() ? str.ToString().Trim() : null; } // reapply if (this.Value == null) { if (!unavailableTokens.Any() && !errors.Any()) { throw new InvalidOperationException($"Could not apply tokens to string '{this.Raw}', but no invalid tokens or errors were reported."); // sanity check, should never happen } this.State.AddUnreadyTokens(unavailableTokens.ToArray()); this.State.AddErrors(errors.ToArray()); } return (this.Value != wasValue || this.State.IsReady != wasReady); }