Пример #1
0
 /// <summary>Assert that an input argument is valid for the value provider.</summary>
 /// <param name="input">The input argument to check, if applicable.</param>
 /// <exception cref="InvalidOperationException">The input argument doesn't match this value provider, or does not respect <see cref="AllowsInput"/> or <see cref="RequiresInput"/>.</exception>
 protected void AssertInputArgument(ITokenString input)
 {
     if (this.RequiresInput && !input.IsMeaningful())
     {
         throw new InvalidOperationException($"The '{this.Name}' token requires an input argument.");
     }
     if (!this.AllowsInput && input.IsMeaningful())
     {
         throw new InvalidOperationException($"The '{this.Name}' token does not allow input arguments.");
     }
 }
Пример #2
0
        /*********
        ** Private methods
        *********/
        /// <summary>Calculate the result of a mathematical expression.</summary>
        /// <param name="input">The input expression.</param>
        /// <param name="result">The result of the calculation.</param>
        /// <param name="error">The error indicating why parsing failed, if applicable.</param>
        private bool TryCalculate(ITokenString input, out object result, out string error)
        {
            // get cached value
            result = 0;
            if (!input.IsMeaningful() || this.Cache.TryGetValue(input.Value, out result))
            {
                error = null;
                return(true);
            }

            // recalculate
            try
            {
                this.Cache[input.Value] = result = this.DataTable.Compute(input.Value, string.Empty);
                error = null;
                return(true);
            }
            catch (Exception ex)
            {
                string reason = ex.Message;
                reason = Regex.Replace(reason, @"Cannot find column \[([^\]]+)]\.", "invalid expression '$1'.");

                result = 0;
                error  = $"Can't parse '{input.Value}' as a math expression: {reason}";
                return(false);
            }
        }
 /// <summary>Get whether the token always chooses from a set of known values for the given input.</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 = input.IsMeaningful()
         ? InvariantHashSet.Boolean()
         : this.GetValidInputs();
     return(true);
 }
Пример #4
0
        /// <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)
        {
            // validate input
            if (input.IsMeaningful())
            {
                // check if input allowed
                if (!this.AllowsInput)
                {
                    error = $"invalid input argument ({input}), token {this.Name} doesn't allow input.";
                    return(false);
                }

                // check value
                InvariantHashSet validInputs = this.GetValidInputs();
                if (validInputs?.Any() == true)
                {
                    if (!validInputs.Contains(input.Value))
                    {
                        error = $"invalid input argument ({(input.Raw != input.Value ? $"{input.Raw} => {input.Value}" : input.Value)}) for {this.Name} token, expected any of {string.Join(", ", validInputs)}";
                        return(false);
                    }
                }
            }

            // no issues found
            error = null;
            return(true);
        }
Пример #5
0
 /// <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 = input.IsMeaningful()
         ? InvariantHashSet.Boolean()
         : this.AllowedRootValues;
     return(allowedValues != null);
 }
        /*********
        ** Private methods
        *********/
        /// <summary>Get whether the given file path exists.</summary>
        /// <param name="input">The relative file path.</param>
        /// <exception cref="InvalidOperationException">The path is not relative or contains directory climbing (../).</exception>
        private bool GetPathExists(ITokenString input)
        {
            if (!input.IsMeaningful() || !input.IsReady)
            {
                return(false);
            }

            // get normalised path
            string path = PathUtilities.NormalisePathSeparators(input.Value);

            // validate
            if (Path.IsPathRooted(path))
            {
                throw new InvalidOperationException($"The {ConditionType.HasFile} token requires a relative path.");
            }
            if (!PathUtilities.IsSafeRelativePath(path))
            {
                throw new InvalidOperationException($"The {ConditionType.HasFile} token requires a relative path and cannot contain directory climbing (../).");
            }

            // check file existence
            string fullPath = Path.Combine(this.ModFolder, PathUtilities.NormalisePathSeparators(path));

            return(File.Exists(fullPath));
        }
        /// <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)
        {
            if (input.IsMeaningful())
            {
                allowedValues = InvariantHashSet.Boolean();
                return(true);
            }

            allowedValues = null;
            return(false);
        }
        /// <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 override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            if (input.IsMeaningful())
            {
                return new[] { this.Values.Contains(input.Value).ToString() }
            }
            ;
            return(this.Values);
        }
    }
Пример #9
0
        /// <summary>Assert that an input argument is valid.</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>
        protected void AssertInput(ITokenString input)
        {
            bool hasInput = input.IsMeaningful();

            if (!this.CanHaveInput && hasInput)
            {
                throw new InvalidOperationException($"The '{this.Name}' token does not allow input arguments ({InternalConstants.InputArgSeparator}).");
            }
            if (this.RequiresInput && !hasInput)
            {
                throw new InvalidOperationException($"The '{this.Name}' token requires an input argument.");
            }
        }
        /*********
        ** Private methods
        *********/
        /// <summary>Parse the numeric min/max values from a range specifier if it's valid.</summary>
        /// <param name="input">The input argument containing the range specifier.</param>
        /// <param name="min">The parsed min value, if valid.</param>
        /// <param name="max">The parsed max value, if valid.</param>
        /// <param name="error">The error indicating why the range is invalid, if applicable.</param>
        private bool TryParseRange(ITokenString input, out int min, out int max, out string error)
        {
            min = 0;
            max = 0;

            // check if input provided
            if (!input.IsMeaningful())
            {
                error = $"invalid input argument ({input.Value}), token {this.Name} requires non-blank input.";
                return(false);
            }

            // split input
            string[] parts = input.SplitValuesNonUnique().ToArray();
            if (parts.Length != 2)
            {
                error = $"invalid input argument ({input.Value}), must specify a minimum and maximum value like {{{{{this.Name}:0,20}}}}.";
                return(false);
            }

            // parse min/max values
            if (!int.TryParse(parts[0], out min))
            {
                error = $"invalid input argument ({input.Value}), can't parse min value '{parts[0]} as an integer.";
                return(false);
            }
            if (!int.TryParse(parts[1], out max))
            {
                error = $"invalid input argument ({input.Value}), can't parse max value '{parts[1]} as an integer.";
                return(false);
            }

            // validate range
            if (min > max)
            {
                error = $"invalid input argument ({input.Value}), min value '{min}' can't be greater than max value '{max}'.";
                return(false);
            }

            int count = (max - min) + 1;

            if (count > RangeValueProvider.MaxCount)
            {
                error = $"invalid input argument ({input.Value}), range can't exceed {RangeValueProvider.MaxCount} numbers (specified range would contain {count} numbers).";
                return(false);
            }

            error = null;
            return(true);
        }
        /// <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 override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            if (!this.IsReady)
            {
                return(Enumerable.Empty <string>());
            }

            if (!input.IsMeaningful())
            {
                return(this.Values.ToArray());
            }

            return(new[] { this.Values.Contains(input.Value).ToString() });
        }
Пример #12
0
        /// <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 token, or does not respect <see cref="IValueProvider.AllowsInput"/> or <see cref="IValueProvider.RequiresInput"/>.</exception>
        public override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            if (input.IsMeaningful())
            {
                bool hasProfession = this.TryParseEnum(input.Value, out Profession profession, mustBeNamed: false) && this.Professions.Contains(profession);
                yield return(hasProfession.ToString());
            }
            else
            {
                foreach (Profession profession in this.Professions)
                {
                    yield return(profession.ToString());
                }
            }
        }
Пример #13
0
        /// <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 override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            if (input.IsMeaningful())
            {
                if (this.Values.TryGetValue(input.Value, out string value))
                {
                    yield return(value);
                }
            }
            else
            {
                foreach (var pair in this.Values)
                {
                    yield return($"{pair.Key}:{pair.Value}");
                }
            }
        }
Пример #14
0
        /// <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 override bool TryValidate(ITokenString input, string value, out string error)
        {
            if (!base.TryValidate(input, value, out error))
            {
                return(false);
            }

            // validate profession IDs
            string profession = input.IsMeaningful() ? input.Value : value;

            if (!this.TryParseEnum(profession, out Profession _, mustBeNamed: false))
            {
                error = $"can't parse '{profession}' as a profession ID; must be one of [{string.Join(", ", Enum.GetNames(typeof(Profession)).OrderByIgnoreCase(p => p))}] or an integer ID.";
                return(false);
            }

            error = null;
            return(true);
        }
Пример #15
0
        /// <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 override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            if (input.IsMeaningful())
            {
                if (this.TryParseEnum(input.Value, out Skill skill) && this.SkillLevels.TryGetValue(skill, out int level))
                {
                    yield return(level.ToString());
                }
            }
            else
            {
                foreach (var pair in this.SkillLevels)
                {
                    yield return($"{pair.Key}:{pair.Value}");
                }
            }
        }
        /// <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 override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            if (input.IsMeaningful())
            {
                bool hasItem = this.TryParseEnum(input.Value, out WalletItem item) && this.Values[item];
                yield return(hasItem.ToString());
            }
            else
            {
                foreach (KeyValuePair <WalletItem, bool> pair in this.Values)
                {
                    if (pair.Value)
                    {
                        yield return(pair.Key.ToString());
                    }
                }
            }
        }
Пример #17
0
        /// <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 override IEnumerable <string> GetValues(ITokenString input)
        {
            this.AssertInputArgument(input);

            yield return(input.IsMeaningful().ToString());
        }
 /// <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);
 }
Пример #19
0
 /// <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);
 }