コード例 #1
0
ファイル: MetaMetadataCache.cs プロジェクト: MarkMpn/Sql4Cds
        /// <inheritdoc/>
        public bool TryGetValue(string logicalName, out EntityMetadata metadata)
        {
            if (_customMetadata.TryGetValue(logicalName, out metadata))
            {
                return(true);
            }

            return(_inner.TryGetValue(logicalName, out metadata));
        }
コード例 #2
0
ファイル: Autocomplete.cs プロジェクト: scubaracer/Sql4Cds
        /// <summary>
        /// Gets the list of Intellisense suggestions to show
        /// </summary>
        /// <param name="text">The current query text</param>
        /// <param name="pos">The index of the character in the <paramref name="text"/> that has just been entered</param>
        /// <param name="currentLength">The length of the current word that is being auto-completed</param>
        /// <returns>A sequence of suggestions to be shown to the user</returns>
        public IEnumerable <string> GetSuggestions(string text, int pos, out int currentLength)
        {
            // If we're in the first word after a FROM or JOIN, show a list of table names
            string currentWord  = null;
            string prevWord     = null;
            string prevPrevWord = null;

            foreach (var word in ReverseWords(text, pos))
            {
                if (currentWord == null)
                {
                    currentWord = word;
                }
                else if (prevWord == null)
                {
                    prevWord = word;
                }
                else if (prevPrevWord == null)
                {
                    prevPrevWord = word;
                    break;
                }
            }

            currentLength = currentWord.Length;

            if (prevWord == null)
            {
                return(Array.Empty <string>());
            }

            switch (prevWord.ToLower())
            {
            case "from":
            case "into":
                // Show table list
                if (_entities != null)
                {
                    return(_entities.Select(x => x.LogicalName + "?4").Where(x => x.StartsWith(currentWord)).OrderBy(x => x));
                }
                break;

            default:
                // Find the FROM clause
                var    words             = new List <string>();
                var    foundFrom         = false;
                var    foundQueryStart   = false;
                var    foundPossibleFrom = false;
                string clause            = null;

                foreach (var word in ReverseWords(text, pos))
                {
                    switch (word.ToLower())
                    {
                    case "from":
                        foundFrom = true;
                        clause    = clause ?? "from";
                        break;

                    case "select":
                        foundQueryStart = true;
                        break;

                    case "update":
                    case "delete":
                        foundPossibleFrom = true;
                        foundQueryStart   = true;
                        break;

                    case "join":
                        clause = clause ?? "join";
                        words.Insert(0, word);
                        break;

                    case "on":
                        clause = clause ?? "on";
                        words.Insert(0, word);
                        break;

                    case "where":
                    case "(":
                        words.Clear();
                        break;

                    case "order":
                    case "group":
                        break;

                    case "set":
                        words.Clear();
                        clause = clause ?? "set";
                        break;

                    case "insert":
                    case "into":
                        clause          = clause ?? "insert";
                        foundQueryStart = true;
                        foundFrom       = true;
                        break;

                    default:
                        if (!String.IsNullOrEmpty(word))
                        {
                            words.Insert(0, word);
                        }
                        break;
                    }

                    if (foundFrom || foundQueryStart)
                    {
                        break;
                    }
                }

                if (!foundFrom)
                {
                    var nextWords     = new List <string>();
                    var foundQueryEnd = false;

                    foreach (var word in Words(text, pos))
                    {
                        switch (word.ToLower())
                        {
                        case "from":
                            foundFrom = true;
                            break;

                        case "group":
                        case "order":
                        case "select":
                        case "insert":
                        case "update":
                        case "delete":
                            foundQueryEnd = true;
                            break;

                        default:
                            if (foundFrom)
                            {
                                nextWords.Add(word);
                            }
                            break;
                        }

                        if (foundQueryEnd)
                        {
                            break;
                        }
                    }

                    if (foundFrom)
                    {
                        words = nextWords;
                    }
                }

                IDictionary <string, string> tables = null;

                if (foundFrom || (foundPossibleFrom && words.Count > 0))
                {
                    // Try to get the table & alias names from the words in the possible FROM clause
                    tables = new Dictionary <string, string>();

                    for (var i = 0; i < words.Count; i++)
                    {
                        var tableName = words[i];
                        var alias     = tableName;

                        if (i < words.Count - 1)
                        {
                            if (words[i + 1].ToLower() == "as" && i < words.Count - 2)
                            {
                                alias = words[i + 2];
                                i    += 2;
                            }
                            else if (words[i + 1].ToLower() != "on" && words[i + 1].ToLower() != "left" && words[i + 1].ToLower() != "inner" && words[i + 1].ToLower() != "right" && words[i + 1].ToLower() != "join" && words[i + 1].ToLower() != "full")
                            {
                                alias = words[i + 1];
                                i++;
                            }
                        }

                        tables[alias] = tableName;

                        while (i < words.Count && words[i].ToLower() != "join")
                        {
                            i++;
                        }
                    }

                    // Start loading all the appropriate metadata in the background
                    foreach (var table in tables.Values)
                    {
                        if (_entities.Any(e => e.LogicalName.Equals(table, StringComparison.OrdinalIgnoreCase)))
                        {
                            _metadata.TryGetValue(table, out _);
                        }
                    }
                }

                if (!prevWord.EndsWith(".") &&
                    !prevWord.EndsWith(",") &&
                    !prevWord.EndsWith("(") &&
                    !prevWord.EndsWith("+") &&
                    !prevWord.EndsWith("-") &&
                    !prevWord.EndsWith("*") &&
                    !prevWord.EndsWith("/") &&
                    !prevWord.EndsWith("=") &&
                    !prevWord.EndsWith(">") &&
                    !prevWord.EndsWith("<") &&
                    !prevWord.Equals("and", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("or", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("select", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("case", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("when", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("like", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("where", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("on", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("by", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("having", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("update", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("delete", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("set", StringComparison.OrdinalIgnoreCase) &&
                    !prevWord.Equals("join", StringComparison.OrdinalIgnoreCase) &&
                    !prevPrevWord.Equals("top", StringComparison.OrdinalIgnoreCase))
                {
                    return(Array.Empty <string>());
                }

                if (tables != null)
                {
                    if (prevWord.Equals("join", StringComparison.OrdinalIgnoreCase))
                    {
                        // Suggest known relationships from the entities already in the FROM clause, followed by a list of all entities
                        // Exclude the table that's currently being entered from the suggestion sources
                        tables.Remove(currentWord);
                        var joinSuggestions = new List <string>();

                        foreach (var table in tables)
                        {
                            if (_metadata.TryGetValue(table.Value, out var metadata))
                            {
                                if (metadata.OneToManyRelationships != null)
                                {
                                    joinSuggestions.AddRange(metadata.OneToManyRelationships.Select(rel => $"{rel.ReferencingEntity}{GetUniqueTableAlias(rel.ReferencingEntity, tables)} ON {table.Key}.{rel.ReferencedAttribute} = {GetUniqueTableName(rel.ReferencingEntity, tables)}.{rel.ReferencingAttribute}?19"));
                                }

                                if (metadata.ManyToOneRelationships != null)
                                {
                                    joinSuggestions.AddRange(metadata.ManyToOneRelationships.Select(rel => $"{rel.ReferencedEntity}{GetUniqueTableAlias(rel.ReferencedEntity, tables)} ON {table.Key}.{rel.ReferencingAttribute} = {GetUniqueTableName(rel.ReferencedEntity, tables)}.{rel.ReferencedAttribute}?18"));
                                }
                            }
                        }

                        joinSuggestions.Sort();

                        joinSuggestions.AddRange(_entities.Select(e => e.LogicalName + "?4").OrderBy(name => name));
                        return(joinSuggestions.Where(s => s.StartsWith(currentWord)));
                    }

                    var additionalSuggestions = (IEnumerable <string>)Array.Empty <string>();

                    if (prevWord.Equals("on", StringComparison.OrdinalIgnoreCase) && _metadata.TryGetValue(tables[prevPrevWord], out var newTableMetadata))
                    {
                        // Suggest known relationships from the other entities in the FROM clause, followed by the normal list of attributes
                        additionalSuggestions = new List <string>();

                        if (newTableMetadata.OneToManyRelationships != null)
                        {
                            ((List <string>)additionalSuggestions).AddRange(newTableMetadata.OneToManyRelationships.SelectMany(rel => tables.Where(table => table.Key != prevPrevWord && table.Value == rel.ReferencingEntity).Select(table => $"{table.Key}.{rel.ReferencingAttribute} = {prevPrevWord}.{rel.ReferencedAttribute}?18")));
                        }

                        if (newTableMetadata.ManyToOneRelationships != null)
                        {
                            ((List <string>)additionalSuggestions).AddRange(newTableMetadata.ManyToOneRelationships.SelectMany(rel => tables.Where(table => table.Key != prevPrevWord && table.Value == rel.ReferencedEntity).Select(table => $"{table.Key}.{rel.ReferencedAttribute} = {prevPrevWord}.{rel.ReferencingAttribute}?19")));
                        }

                        ((List <string>)additionalSuggestions).Sort();
                    }

                    if (prevWord.Equals("update", StringComparison.OrdinalIgnoreCase) ||
                        prevWord.Equals("delete", StringComparison.OrdinalIgnoreCase))
                    {
                        return(tables.Keys.Select(x => x + "?4").Where(x => x.StartsWith(currentWord)).OrderBy(x => x));
                    }

                    if (clause == "set" && (prevWord.Equals("set", StringComparison.OrdinalIgnoreCase) || prevWord == ","))
                    {
                        var targetTable = "";

                        foreach (var word in ReverseWords(text, pos))
                        {
                            if (word.Equals("update", StringComparison.OrdinalIgnoreCase))
                            {
                                break;
                            }

                            targetTable = word;
                        }

                        if (tables.TryGetValue(targetTable, out var tableName) && _metadata.TryGetValue(tableName, out var metadata))
                        {
                            return(metadata.Attributes.Where(a => a.IsValidForUpdate != false && a.LogicalName.StartsWith(currentWord, StringComparison.OrdinalIgnoreCase)).Select(a => a.LogicalName + GetIconIndex(a)).OrderBy(a => a));
                        }
                    }

                    if (currentWord.Contains("."))
                    {
                        // Autocomplete list is attributes in the current table
                        var alias = currentWord.Substring(0, currentWord.IndexOf('.'));
                        currentWord   = currentWord.Substring(currentWord.IndexOf('.') + 1);
                        currentLength = currentWord.Length;

                        if (tables.TryGetValue(alias, out var tableName))
                        {
                            if (_metadata.TryGetValue(tableName, out var metadata))
                            {
                                return(metadata.Attributes.Select(x => x.LogicalName + GetIconIndex(x)).Where(x => x.StartsWith(currentWord)).OrderBy(x => x));
                            }
                        }
                    }
                    else if (clause == "join")
                    {
                        // Entering a table alias, nothing useful to auto-complete
                    }
                    else
                    {
                        // Autocomplete list is:
                        // * table/alias names
                        // * attribute names unique across tables
                        // * functions
                        var items = new List <string>();

                        if (clause != "insert")
                        {
                            items.AddRange(tables.Keys.Select(x => x + "?4"));
                        }

                        var attributes = new List <AttributeMetadata>();

                        foreach (var table in tables)
                        {
                            if (_metadata.TryGetValue(table.Value, out var metadata))
                            {
                                attributes.AddRange(metadata.Attributes);
                            }
                        }

                        items.AddRange(attributes.GroupBy(x => x.LogicalName).Where(g => g.Count() == 1).Select(g => g.Key + GetIconIndex(g.First())));
                        items.Sort();

                        return(additionalSuggestions.Concat(items.Where(x => x.StartsWith(currentWord)).OrderBy(x => x)));
                    }
                }
                else if (prevWord.Equals("update", StringComparison.OrdinalIgnoreCase))
                {
                    return(_entities.Select(x => x.LogicalName + "?4").Where(x => x.StartsWith(currentWord)).OrderBy(x => x));
                }

                break;
            }

            return(Array.Empty <string>());
        }