Esempio n. 1
0
        /// <summary>
        /// Aplicação do template estendido para a sintaxe MANY MATCHES:
        /// -   MANY [@parametro] [NOT] MATCHES [IF SET] ( ... )
        /// </summary>
        /// <param name="text">O texto que sofrerá a substituição.</param>
        /// <param name="args">Argumentos disponíveis.</param>
        /// <param name="keyGen">Algoritmo de geração de nomes de parâmetros.</param>
        /// <returns></returns>
        private static string ReplaceManyMatches(string text, IDictionary <string, object> args, KeyGen keyGen)
        {
            // Aplicando a substituição da instrução:
            //   many [target] [not] matches [if set] ()
            var regex   = new Regex(@"[(\s]many\s+(?:@([\w]+)\s+)?(?:(not)\s+)?matches(?:\s+(if\s+set))?\s*?(?:([a-zA-Z_.]+)|([a-zA-Z0-9_]*[(](?>[(](?<c>)|[^()]+|[)](?<-c>))*(?(c)(?!))[)]))");
            var matches = regex.Matches(text);

            foreach (Match match in matches.Cast <Match>().Reverse())
            {
                string replacement = "";

                // Exemplos de textos extraidos
                //
                // " many matches ( ... ) "
                //   [1] ""
                //   [2] ""
                //   [3] ""
                //   -inutil-
                //   [5] "( ... )"
                //
                // " many @parametro not matches if set ( ... ) "
                //   [1] "parametro"
                //   [2] "not"
                //   [3] "if set"
                //   -inutil-
                //   [5] "( ... )"

                var bagName    = match.Groups[1].Value;
                var isNot      = (match.Groups[2].Length > 0);
                var isOptional = (match.Groups[3].Length > 0);
                var body       = match.Groups[5].Value;
                body = body.Substring(1, body.Length - 2);

                var candidate  = args.Get(bagName);
                var enumerable = (candidate as IEnumerable) ?? ((candidate as Var)?.Value as IEnumerable);
                var bags       = enumerable?.OfType <IDictionary <string, object> >();
                if (bags?.Any() != true)
                {
                    replacement = isOptional ? "1=1" : "1=0";
                }
                else
                {
                    var sentences = new List <string>();
                    foreach (var bag in bags)
                    {
                        var sentence = body;

                        var nestGen = keyGen.Derive();
                        foreach (var key in bag.Keys)
                        {
                            var matches2 = Regex.Matches(sentence, $@"@({key}\w?)");
                            foreach (Match match2 in matches2.Cast <Match>().Reverse())
                            {
                                var matchKey = match2.Groups[1].Value;
                                if (matchKey != key)
                                {
                                    continue;
                                }

                                var index = match2.Groups[1].Index;
                                var count = match2.Groups[1].Length;

                                var keyName  = nestGen.DeriveName(key);
                                var keyValue = bag[key];

                                args[keyName] = keyValue;
                                sentence      = sentence.Stuff(index, count, keyName);
                            }
                        }

                        sentence = ReplaceTemplates(sentence, args, nestGen);
                        sentence = $"{(isNot ? " not " : "")}({sentence})";
                        sentences.Add(sentence);
                    }

                    replacement = $" ({string.Join(" or ", sentences.Distinct())})";
                }

                text = text.Stuff(match.Index, match.Length, $" {replacement}");
            }
            return(text);
        }
Esempio n. 2
0
        /// <summary>
        /// Constrói a condição que substituirá o template baseado na instrução:
        /// - target [always] matches @parametro
        ///
        /// Se o valor do parâmetro não tiver sido definido o critério retornado será nulo.
        ///
        /// O critério criado contém o termo "{0}" que deve ser substituído pela
        /// parte "target" à direta da instrução matches.
        ///
        /// Por exemplo, em:
        ///   Campo matches @nome
        ///
        /// O critério produzir pode ser algo como:
        ///   {0} like @nome
        ///
        /// Que deve então ser substituído com String.Format() pela parte à direta
        /// da instrução, neste caso "Campo":
        ///   String.Format("{0} like @nome", "Campo");
        ///
        /// Produzindo como resultado:
        ///   Campo like @nome
        /// </summary>
        /// <param name="parameter">Nome do parâmetro que será substituído.</param>
        /// <param name="value">Valor atribuído ao parâmetro.</param>
        /// <param name="args">Argumentos disponíveis.</param>
        /// <param name="keyGen">Algoritmo de geração de nomes de parâmetros.</param>
        /// <returns>A condição que substituirá o template.</returns>
        private static string CreateCriteria(string parameter, object value, IDictionary <string, object> args, KeyGen keyGen)
        {
            value = (value as Var)?.Value ?? value;

            if (value == null)
            {
                return(null);
            }

            if (value is string text)
            {
                if (text.HasWildcardPattern())
                {
                    args[parameter] = value;
                    return("{0} like @" + parameter);
                }
                else
                {
                    args[parameter] = value;
                    return("{0} = @" + parameter);
                }
            }

            if (value.GetType().IsValueType)
            {
                args[parameter] = value;
                return("{0} = @" + parameter);
            }

            if (value is Sql nestSql)
            {
                var nestGen  = keyGen.Derive();
                var nestArgs = nestSql.Parameters;
                var nestText = nestSql.Text + "\n";

                // cada parametro recebera um sufixo para diferenciacao de parametros
                // que já existem na instância de Sql.
                foreach (var key in nestArgs.Keys)
                {
                    var matches = Regex.Matches(nestText, $@"@({key}\w?)");
                    foreach (Match match in matches.Cast <Match>().Reverse())
                    {
                        var matchKey = match.Groups[1].Value;
                        if (matchKey != key)
                        {
                            continue;
                        }

                        var index = match.Groups[1].Index;
                        var count = match.Groups[1].Length;

                        var keyName  = nestGen.DeriveName(key);
                        var keyValue = nestArgs[key];

                        args[keyName] = keyValue;
                        nestText      = nestText.Stuff(index, count, keyName);
                    }
                }

                return("{0} in (" + nestText + ")");
            }

            if (value is IEnumerable list)
            {
                var items  = Commander.CreateSqlCompatibleValue(list);
                var values = string.Join(",", (IEnumerable)items);
                return("{0} in (" + values + ")");
            }

            if (value is Range range)
            {
                if (range.Min != null && range.Max != null)
                {
                    var minArg = keyGen.DeriveName(parameter);
                    var maxArg = keyGen.DeriveName(parameter);
                    args[minArg] = range.Min;
                    args[maxArg] = range.Max;
                    return("{0} between @" + minArg + " and @" + maxArg);
                }

                if (range.Min != null)
                {
                    var minArg = keyGen.DeriveName(parameter);
                    args[minArg] = range.Min;
                    return("{0} >= @" + minArg);
                }

                if (range.Max != null)
                {
                    var name = keyGen.DeriveName(parameter);
                    args[name] = range.Max;
                    return("{0} <= @" + name);
                }

                args[parameter] = value;
                return("{0} = @" + parameter);
            }

            args[parameter] = value;
            return("{0} = @" + parameter);
        }
Esempio n. 3
0
        /// <summary>
        /// Aplica a substituição dos parâmetros para construção da instrução SQL definitiva.
        /// </summary>
        /// <param name="text">O texto que sofrerá a substituição.</param>
        /// <param name="args">Argumentos disponíveis.</param>
        /// <param name="keyGen">Algoritmo de geração de nomes de parâmetros.</param>
        /// <returns></returns>
        private static string ReplaceTemplates(string text, IDictionary <string, object> args, KeyGen keyGen)
        {
            text = ReplaceManyMatches(text, args, keyGen);

            var extendedParameters = ExtractKnownExtendedParameters(text).ToArray();
            var names =
                from name in extendedParameters
                orderby name.Length descending, name
            select name;

            foreach (var name in names)
            {
                var value    = args.Get(name);
                var criteria = CreateCriteria(name, value, args, keyGen);
                text = ReplaceMatches(text, name, criteria);
            }

            var patterns = new[] {
                new[] { @"1=1\sand\s1=1", "1=1" },
                new[] { @"1=1\sand\s1=0", "1=0" },
                new[] { @"1=0\sand\s1=1", "1=0" },
                new[] { @"1=0\sand\s1=0", "1=0" },

                new[] { @"1=1\sor\s1=1", "1=1" },
                new[] { @"1=1\sor\s1=0", "1=1" },
                new[] { @"1=0\sor\s1=1", "1=1" },
                new[] { @"1=0\sor\s1=0", "1=0" },

                new[] { @"1=1\sand\s", "" },
                new[] { @"\sand\s1=1", "" },
                new[] { @"1=0\sor\s", "" },
                new[] { @"\sor\s1=0", "" },

                new[] { @"\sand\s\(1=1\)", "" },
                new[] { @"\(1=1\)\sand\s", "" },
                new[] { @"\sor\s\(1=0\)", "" },
                new[] { @"\(1=0\)\sor\s", "" }
            };

            bool changed;

            do
            {
                changed = false;
                foreach (var pattern in patterns)
                {
                    var oldText     = pattern[0];
                    var newText     = pattern[1];
                    var replacement = Regex.Replace(text, oldText, newText);
                    if (replacement != text)
                    {
                        text    = replacement;
                        changed = true;
                    }
                }
            } while (changed);

            return(text);
        }