/// <summary>Validate that the provided values are valid for the input argument (regardless of whether they match).</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="values">The values to validate.</param> /// <param name="error">The validation error, if any.</param> /// <returns>Returns whether validation succeeded.</returns> public bool TryValidateValues(ITokenString input, InvariantHashSet values, out string error) { if (!this.TryValidateInput(input, out error)) { return(false); } // default validation { InvariantHashSet validValues = this.GetAllowedValues(input); if (validValues?.Any() == true) { string[] invalidValues = values .Where(p => !validValues.Contains(p)) .Distinct() .ToArray(); if (invalidValues.Any()) { error = $"invalid values ({string.Join(", ", invalidValues)}); expected one of {string.Join(", ", validValues)}"; return(false); } } } // custom validation foreach (string value in values) { if (!this.TryValidate(input, value, out error)) { return(false); } } // no issues found error = null; return(true); }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="position">The tile position to edit, relative to the top-left corner.</param> /// <param name="layer">The map layer name to edit.</param> /// <param name="setIndex">The tilesheet index to apply, the string <c>false</c> to remove it, or null to leave it as-is.</param> /// <param name="setTilesheet">The tilesheet ID to set.</param> /// <param name="setProperties">The tile properties to set.</param> /// <param name="remove">Whether to remove the current tile and all its properties.</param> public EditMapPatchTile(TokenPosition position, IManagedTokenString layer, IManagedTokenString setIndex, IManagedTokenString setTilesheet, IDictionary <IManagedTokenString, IManagedTokenString> setProperties, IManagedTokenString remove) { this.Position = position; this.Layer = layer; this.SetIndex = setIndex; this.SetTilesheet = setTilesheet; this.SetProperties = setProperties?.ToDictionary(p => (ITokenString)p.Key, p => (ITokenString)p.Value); this.Remove = remove; this.Contextuals = new AggregateContextual() .Add(position) .Add(layer) .Add(setIndex) .Add(setTilesheet) .Add(remove); if (setProperties != null) { foreach (var pair in setProperties) { this.Contextuals.Add(pair.Key).Add(pair.Value); } } }
/// <summary>Get whether the token always chooses from a set of known values for the given input. Mutually exclusive with <see cref="IValueProvider.HasBoundedRangeValues"/>.</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="allowedValues">The possible values for the input.</param> /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="IValueProvider.AllowsInput"/> or <see cref="IValueProvider.RequiresInput"/>.</exception> public override bool HasBoundedValues(ITokenString input, out InvariantHashSet allowedValues) { allowedValues = new InvariantHashSet(this.GetValues(input)); return(true); }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="logName">A unique name for this patch shown in log messages.</param> /// <param name="contentPack">The content pack which requested the patch.</param> /// <param name="assetName">The normalised asset name to intercept.</param> /// <param name="conditions">The conditions which determine whether this patch should be applied.</param> /// <param name="fromAsset">The asset key to load from the content pack instead.</param> /// <param name="fromArea">The map area from which to read tiles.</param> /// <param name="toArea">The map area to overwrite.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> /// <param name="normaliseAssetName">Normalise an asset name.</param> public EditMapPatch(string logName, ManagedContentPack contentPack, ITokenString assetName, IEnumerable <Condition> conditions, ITokenString fromAsset, Rectangle fromArea, Rectangle toArea, IMonitor monitor, Func <string, string> normaliseAssetName) : base(logName, PatchType.EditMap, contentPack, assetName, conditions, normaliseAssetName, fromAsset: fromAsset) { this.FromArea = fromArea != Rectangle.Empty ? fromArea : null as Rectangle?; this.ToArea = toArea != Rectangle.Empty ? toArea : null as Rectangle?; this.Monitor = monitor; }
/// <summary>Whether the value provider may return multiple values for the given input.</summary> /// <param name="input">The input argument, if applicable.</param> public bool CanHaveMultipleValues(ITokenString input = null) { return(input.IsMeaningful() ? this.CanHaveMultipleValuesForInput : this.CanHaveMultipleValuesForRoot); }
/// <summary>Get the current values.</summary> /// <param name="input">The input argument, if applicable.</param> /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="IValueProvider.AllowsInput"/> or <see cref="IValueProvider.RequiresInput"/>.</exception> public virtual IEnumerable <string> GetValues(ITokenString input) { this.AssertInputArgument(input); yield break; }
/// <summary>Set the current values.</summary> /// <param name="values">The values to set.</param> public void SetValue(ITokenString values) { this.DynamicValues.SetValue(values); }
/// <summary>Validate that the provided input argument is valid.</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="error">The validation error, if any.</param> /// <returns>Returns whether validation succeeded.</returns> public bool TryValidateInput(ITokenString input, out string error) { return(this.Values.TryValidateInput(input, out error)); }
/// <summary>Get unique comma-separated values from a token string.</summary> /// <param name="tokenStr">The token string to parse.</param> /// <exception cref="InvalidOperationException">The token string is not ready (<see cref="IContextual.IsReady"/> is false).</exception> public static InvariantHashSet SplitValuesUnique(this ITokenString tokenStr) { return(new InvariantHashSet(tokenStr.SplitValuesNonUnique())); }
/// <summary>Get the allowed values for an input argument (or <c>null</c> if any value is allowed).</summary> /// <param name="input">The input argument, if any.</param> /// <exception cref="InvalidOperationException">The input does not respect <see cref="IToken.CanHaveInput"/> or <see cref="IToken.RequiresInput"/>.</exception> public virtual InvariantHashSet GetAllowedValues(ITokenString input) { return(this.Values.GetAllowedValues(input)); }
/// <summary>Prepare a local asset file for a patch to use.</summary> /// <param name="pack">The content pack being loaded.</param> /// <param name="path">The asset path in the content patch.</param> /// <param name="tokenContext">The tokens available for this content pack.</param> /// <param name="migrator">The migrator which validates and migrates content pack data.</param> /// <param name="error">The error reason if preparing the asset fails.</param> /// <param name="tokenedPath">The parsed value.</param> /// <returns>Returns whether the local asset was successfully prepared.</returns> private bool TryPrepareLocalAsset(ManagedContentPack pack, string path, IContext tokenContext, IMigration migrator, out string error, out ITokenString tokenedPath) { // normalise raw value path = this.NormaliseLocalAssetPath(pack, path); if (path == null) { error = $"must set the {nameof(PatchConfig.FromFile)} field for this action type."; tokenedPath = null; return(false); } // tokenise if (!this.TryParseStringTokens(path, tokenContext, migrator, out string tokenError, out tokenedPath)) { error = $"the {nameof(PatchConfig.FromFile)} is invalid: {tokenError}"; tokenedPath = null; return(false); } // looks OK error = null; return(true); }
/// <summary>Parse a string which can contain tokens, and validate that it's valid.</summary> /// <param name="rawValue">The raw string which may contain tokens.</param> /// <param name="tokenContext">The tokens available for this content pack.</param> /// <param name="migrator">The migrator which validates and migrates content pack data.</param> /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param> /// <param name="parsed">The parsed value.</param> private bool TryParseStringTokens(string rawValue, IContext tokenContext, IMigration migrator, out string error, out ITokenString parsed) { // parse parsed = new TokenString(rawValue, tokenContext); if (!migrator.TryMigrate(parsed, out error)) { return(false); } // validate unknown tokens IContextualState state = parsed.GetDiagnosticState(); if (state.InvalidTokens.Any()) { error = $"found unknown tokens ({string.Join(", ", state.InvalidTokens.OrderBy(p => p))})"; parsed = null; return(false); } // validate tokens foreach (LexTokenToken lexToken in parsed.GetTokenPlaceholders(recursive: false)) { IToken token = tokenContext.GetToken(lexToken.Name, enforceContext: false); if (token == null) { error = $"'{lexToken}' can't be used as a token because that token could not be found."; // should never happen parsed = null; return(false); } } // looks OK error = null; return(true); }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="tokenString">The underlying tokenized string.</param> public InputArguments(ITokenString tokenString) { this.TokenString = tokenString; }
/// <summary>Get the current values of the given token for comparison.</summary> /// <param name="name">The token name.</param> /// <param name="input">The input argument, if any.</param> /// <param name="enforceContext">Whether to only consider tokens that are available in the context.</param> /// <returns>Return the values of the matching token, or an empty list if the token doesn't exist.</returns> /// <exception cref="ArgumentNullException">The specified key is null.</exception> public IEnumerable <string> GetValues(string name, ITokenString input, bool enforceContext) { IToken token = this.GetToken(name, enforceContext); return(token?.GetValues(input) ?? new string[0]); }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="logName">A unique name for this patch shown in log messages.</param> /// <param name="contentPack">The content pack which requested the patch.</param> /// <param name="assetName">The normalised asset name to intercept.</param> /// <param name="conditions">The conditions which determine whether this patch should be applied.</param> /// <param name="localAsset">The asset key to load from the content pack instead.</param> /// <param name="normaliseAssetName">Normalise an asset name.</param> public LoadPatch(string logName, ManagedContentPack contentPack, ITokenString assetName, IEnumerable <Condition> conditions, ITokenString localAsset, Func <string, string> normaliseAssetName) : base(logName, PatchType.Load, contentPack, assetName, conditions, normaliseAssetName, fromAsset: localAsset) { }
/// <summary>Get the current token values.</summary> /// <param name="input">The input to check, if any.</param> /// <exception cref="InvalidOperationException">The input does not respect <see cref="IToken.CanHaveInput"/> or <see cref="IToken.RequiresInput"/>.</exception> public virtual IEnumerable <string> GetValues(ITokenString input) { this.AssertInput(input); return(this.Values.GetValues(input)); }
/// <summary>Whether the token may return multiple values for the given name.</summary> /// <param name="input">The input argument, if any.</param> public bool CanHaveMultipleValues(ITokenString input) { return(this.Values.CanHaveMultipleValues(input)); }
/********* ** Protected methods *********/ /// <summary>Construct an instance.</summary> /// <param name="logName">A unique name for this patch shown in log messages.</param> /// <param name="type">The patch type.</param> /// <param name="contentPack">The content pack which requested the patch.</param> /// <param name="assetName">The normalized asset name to intercept.</param> /// <param name="conditions">The conditions which determine whether this patch should be applied.</param> /// <param name="normalizeAssetName">Normalize an asset name.</param> /// <param name="fromAsset">The normalized asset key from which to load the local asset (if applicable), including tokens.</param> protected Patch(string logName, PatchType type, ManagedContentPack contentPack, ITokenString assetName, IEnumerable <Condition> conditions, Func <string, string> normalizeAssetName, ITokenString fromAsset = null) { this.LogName = logName; this.Type = type; this.ContentPack = contentPack; this.RawTargetAsset = assetName; this.Conditions = conditions.ToArray(); this.NormalizeAssetNameImpl = normalizeAssetName; this.PrivateContext = new LocalContext(scope: this.ContentPack.Manifest.UniqueID); this.RawFromAsset = fromAsset; this.Contextuals .Add(this.Conditions) .Add(this.RawTargetAsset) .Add(this.RawFromAsset); }
/// <summary>Add a set of possible values.</summary> /// <param name="possibleValues">The possible values to add.</param> public void AddAllowedValues(ITokenString possibleValues) { this.DynamicValues.AddAllowedValues(possibleValues); this.CanHaveMultipleRootValues = this.DynamicValues.CanHaveMultipleValues(); }
/// <summary>Validate that the provided input argument is valid.</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="error">The validation error, if any.</param> /// <returns>Returns whether validation succeeded.</returns> public override bool TryValidateInput(ITokenString input, out string error) { return(this.TryCalculate(input, out _, out error)); }
/// <summary>Validate that the provided input argument is valid.</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="error">The validation error, if any.</param> /// <returns>Returns whether validation succeeded.</returns> public override bool TryValidateInput(ITokenString input, out string error) { return (base.TryValidateInput(input, out error) && this.TryParseRange(input, out _, out _, out error)); }
/**** ** Tokens ****/ /// <summary>Get whether a token string has a meaningful value.</summary> /// <param name="str">The token string.</param> public static bool IsMeaningful(this ITokenString str) { return(!string.IsNullOrWhiteSpace(str?.Value)); }
/// <summary>Get the allowed values for an input argument (or <c>null</c> if any value is allowed).</summary> /// <param name="input">The input argument, if applicable.</param> /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="IValueProvider.AllowsInput"/> or <see cref="IValueProvider.RequiresInput"/>.</exception> public virtual InvariantHashSet GetAllowedValues(ITokenString input) { return(null); }
/// <summary>Get unique comma-separated values from a token string.</summary> /// <param name="tokenStr">The token string to parse.</param> /// <param name="normalize">Normalize a value.</param> /// <exception cref="InvalidOperationException">The token string is not ready (<see cref="IContextual.IsReady"/> is false).</exception> public static InvariantHashSet SplitValuesUnique(this ITokenString tokenStr, Func <string, string> normalize = null) { return(new InvariantHashSet(tokenStr.SplitValuesNonUnique(normalize))); }
/// <summary>Validate that the provided value is valid for an input argument (regardless of whether they match).</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="value">The value to validate.</param> /// <param name="error">The validation error, if any.</param> /// <returns>Returns whether validation succeeded.</returns> protected virtual bool TryValidate(ITokenString input, string value, out string error) { error = null; return(true); }
/// <summary>Get the current values of the given token for comparison.</summary> /// <param name="name">The token name.</param> /// <param name="input">The input argument, if any.</param> /// <param name="enforceContext">Whether to only consider tokens that are available in the context.</param> /// <returns>Return the values of the matching token, or an empty list if the token doesn't exist.</returns> /// <exception cref="ArgumentNullException">The specified key is null.</exception> public IEnumerable <string> GetValues(string name, ITokenString input, bool enforceContext) { return(this.GlobalContext.GetValues(name, input, enforceContext)); }
/// <summary>Get the allowed values for an input argument (or <c>null</c> if any value is allowed).</summary> /// <param name="input">The input argument, if applicable.</param> /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="IValueProvider.AllowsInput"/> or <see cref="IValueProvider.RequiresInput"/>.</exception> public override InvariantHashSet GetAllowedValues(ITokenString input) { return(input.IsMeaningful() ? InvariantHashSet.Boolean() : this.AllowedRootValues); }
/// <summary>Get whether the token always chooses from a set of known values for the given input. Mutually exclusive with <see cref="IToken.HasBoundedRangeValues"/>.</summary> /// <param name="input">The input argument, if applicable.</param> /// <param name="allowedValues">The possible values for the input.</param> /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="IToken.CanHaveInput"/> or <see cref="IToken.RequiresInput"/>.</exception> public bool HasBoundedValues(ITokenString input, out InvariantHashSet allowedValues) { return(this.Values.HasBoundedValues(input, out allowedValues)); }
/// <summary>Set the current values.</summary> /// <param name="values">The values to set.</param> public void SetValue(ITokenString values) { this.Values = values.SplitValuesUnique(); }
/// <summary>Get whether the token always returns a value within a bounded numeric range for the given input. Mutually exclusive with <see cref="IToken.HasBoundedValues"/>.</summary> /// <param name="input">The input argument, if any.</param> /// <param name="min">The minimum value this token may return.</param> /// <param name="max">The maximum value this token may return.</param> /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="IToken.CanHaveInput"/> or <see cref="IToken.RequiresInput"/>.</exception> public bool HasBoundedRangeValues(ITokenString input, out int min, out int max) { return(this.Values.HasBoundedRangeValues(input, out min, out max)); }