/// <summary>
                /// Implements IBranch.Text
                /// </summary>
                protected string GetText(int row, int column)
                {
                    ProviderBranch parentBranch = myParentBranch;

                    string[] itemStrings = parentBranch.myItemStrings;
                    string   retVal;

                    if (itemStrings != null)
                    {
                        row   += parentBranch.myTargetedTypes[myTargetedTypeIndex].FirstIdentifier;
                        retVal = itemStrings[row];
                        if (retVal == null)
                        {
                            VerbalizationSnippetsIdentifier id = parentBranch.myIdentifiers[row];
                            if (id.IsDefaultIdentifier)
                            {
                                retVal = id.Description;
                            }
                            else
                            {
                                string languageName = id.LanguageId;
                                try
                                {
                                    languageName = CultureInfo.GetCultureInfoByIetfLanguageTag(id.LanguageId).DisplayName;
                                }
                                catch (ArgumentException)
                                {
                                }
                                retVal = string.Format(CultureInfo.CurrentCulture, parentBranch.myLanguageFormatString, id.Description, languageName);
                            }
                            itemStrings[row] = retVal;
                        }
                    }
                    else
                    {
                        retVal = parentBranch.myIdentifiers[parentBranch.myTargetedTypes[myTargetedTypeIndex].FirstIdentifier + row].Description;
                    }
                    return(retVal);
                }
            public ProviderBranch(
                string currentSettings,
                IEnumerable <IVerbalizationTargetProvider> targetProviders,
                IEnumerable <IVerbalizationSnippetsProvider> snippetProviders,
#if VISUALSTUDIO_15_0
                string[] verbalizationDirectories,
#else
                string verbalizationDirectory,
#endif
                string languageFormatString)
            {
                VerbalizationSnippetsIdentifier[] allIdentifiers = VerbalizationSnippetSetsManager.LoadAvailableSnippets(
                    snippetProviders,
#if VISUALSTUDIO_15_0
                    verbalizationDirectories);
#else
                    verbalizationDirectory);
#endif
                VerbalizationSnippetsIdentifier[] currentIdentifiers = VerbalizationSnippetsIdentifier.ParseIdentifiers(currentSettings);

                if (languageFormatString != null)
                {
                    if (languageFormatString.Length != 0)
                    {
                        myLanguageFormatString = languageFormatString;
                    }
                    else
                    {
                        languageFormatString = null;
                    }
                }

                // Gather all targets
                List <VerbalizationTargetData> targetsList = new List <VerbalizationTargetData>();
                foreach (IVerbalizationTargetProvider provider in targetProviders)
                {
                    VerbalizationTargetData[] currentData = provider.ProvideVerbalizationTargets();
                    if (currentData != null)
                    {
                        for (int i = 0; i < currentData.Length; ++i)
                        {
                            targetsList.Add(currentData[i]);
                            // Put it in a condition we can binary search it
                            targetsList.Sort(TargetKeyComparer.Instance);
                        }
                    }
                }
                VerbalizationTargetData[] targets;
                myVerbalizationTargets = targets = targetsList.ToArray();

                // Make sure all identifiers can map to a known target
                int unknownTargetsCount = 0;
                for (int i = 0; i < allIdentifiers.Length; ++i)
                {
                    string identifierTarget = allIdentifiers[i].Target;
                    if (!string.IsNullOrEmpty(identifierTarget) &&
                        0 > Array.BinarySearch <VerbalizationTargetData>(targets, new VerbalizationTargetData(identifierTarget, null), TargetKeyComparer.Instance))
                    {
                        ++unknownTargetsCount;
                        allIdentifiers[i] = default(VerbalizationSnippetsIdentifier);
                    }
                }
                if (unknownTargetsCount != 0)
                {
                    VerbalizationSnippetsIdentifier[] reducedIdentifiers = new VerbalizationSnippetsIdentifier[allIdentifiers.Length - unknownTargetsCount];
                    int currentValidIdentifier = 0;
                    for (int i = 0; i < allIdentifiers.Length; ++i)
                    {
                        VerbalizationSnippetsIdentifier identifier = allIdentifiers[i];
                        if (!identifier.IsEmpty)
                        {
                            reducedIdentifiers[currentValidIdentifier] = identifier;
                            ++currentValidIdentifier;
                        }
                    }
                    allIdentifiers = reducedIdentifiers;
                }

                // Gather all types
                List <SnippetsType> types = new List <SnippetsType>();
                SnippetsType        currentType;
                foreach (IVerbalizationSnippetsProvider provider in snippetProviders)
                {
                    VerbalizationSnippetsData[] currentData = provider.ProvideVerbalizationSnippets();
                    if (currentData != null)
                    {
                        for (int i = 0; i < currentData.Length; ++i)
                        {
                            currentType = default(SnippetsType);
                            currentType.TypeDescription = currentData[i].TypeDescription;
                            currentType.EnumTypeName    = currentData[i].EnumType.FullName;
                            types.Add(currentType);
                        }
                    }
                }

                // Sort first by type description
                types.Sort(
                    delegate(SnippetsType type1, SnippetsType type2)
                {
                    return(string.Compare(type1.TypeDescription, type2.TypeDescription, StringComparison.CurrentCultureIgnoreCase));
                });

                // Sort all identifiers. First by type description on previous sort, then
                // putting the default identifier first, then by explicit target types, then by the identifier description
                int typesCount = types.Count;
                Array.Sort <VerbalizationSnippetsIdentifier>(
                    allIdentifiers,
                    delegate(VerbalizationSnippetsIdentifier identifier1, VerbalizationSnippetsIdentifier identifier2)
                {
                    int retVal       = 0;
                    string typeName1 = identifier1.EnumTypeName;
                    string typeName2 = identifier2.EnumTypeName;
                    if (typeName1 != typeName2)
                    {
                        int location1 = -1;
                        for (int i = 0; i < typesCount; ++i)
                        {
                            if (types[i].EnumTypeName == typeName1)
                            {
                                location1 = i;
                                break;
                            }
                        }
                        int location2 = -1;
                        for (int i = 0; i < typesCount; ++i)
                        {
                            if (types[i].EnumTypeName == typeName2)
                            {
                                location2 = i;
                                break;
                            }
                        }
                        retVal = location1.CompareTo(location2);
                    }
                    if (retVal == 0)
                    {
                        string target1 = identifier1.Target;
                        string target2 = identifier2.Target;
                        if (target1 != target2)
                        {
                            if (target1 == VerbalizationSnippetsIdentifier.DefaultTarget)
                            {
                                retVal = -1;
                            }
                            else if (target2 == VerbalizationSnippetsIdentifier.DefaultTarget)
                            {
                                retVal = 1;
                            }
                            else
                            {
                                retVal = string.Compare(GetVerbalizationTargetDisplayName(target1), GetVerbalizationTargetDisplayName(target2), StringComparison.CurrentCultureIgnoreCase);
                            }
                        }
                    }
                    if (retVal == 0)
                    {
                        bool isDefault1 = identifier1.IsDefaultIdentifier;
                        bool isDefault2 = identifier2.IsDefaultIdentifier;
                        if (isDefault1)
                        {
                            if (!isDefault2)
                            {
                                retVal = -1;
                            }
                        }
                        else if (isDefault2)
                        {
                            retVal = 1;
                        }
                    }
                    if (retVal == 0)
                    {
                        retVal = string.Compare(identifier1.Description, identifier2.Description, StringComparison.CurrentCultureIgnoreCase);
                    }
                    return(retVal);
                });

                // Now get a count of all targeted types
                int    allIdentifiersCount = allIdentifiers.Length;
                int    targetedTypesCount  = 0;
                string lastTarget          = null;        // Start with invalid target
                string lastEnumTypeName    = null;
                for (int i = 0; i < allIdentifiersCount; ++i)
                {
                    string currentTarget = allIdentifiers[i].Target;
                    if (currentTarget != lastTarget)
                    {
                        lastEnumTypeName = allIdentifiers[i].EnumTypeName;
                        lastTarget       = currentTarget;
                        ++targetedTypesCount;
                    }
                    else if (lastEnumTypeName != null)
                    {
                        string currentEnumTypeName = allIdentifiers[i].EnumTypeName;
                        if (currentEnumTypeName != lastEnumTypeName)
                        {
                            lastEnumTypeName = currentEnumTypeName;
                            ++targetedTypesCount;
                        }
                    }
                }

                // Allocate targeted types and bind targeted types to types
                TargetedSnippetsType[] targetedTypes = new TargetedSnippetsType[targetedTypesCount];
                lastTarget = null;
                int currentTypeIndex = -1;
                currentType = default(SnippetsType);
                TargetedSnippetsType currentTargetedType = default(TargetedSnippetsType);
                int currentTargetIndex = -1;
                lastEnumTypeName = null;
                for (int i = 0; i < allIdentifiersCount; ++i)
                {
                    string currentTarget       = allIdentifiers[i].Target;
                    string currentEnumTypeName = allIdentifiers[i].EnumTypeName;
                    bool   enumNameChanged     = lastEnumTypeName == null || currentEnumTypeName != lastEnumTypeName;
                    if (enumNameChanged || currentTarget != lastTarget)
                    {
                        ++currentTargetIndex;
                        lastTarget = currentTarget;
                        if (enumNameChanged)
                        {
                            lastEnumTypeName = currentEnumTypeName;
                            if (currentTypeIndex != -1)
                            {
                                types[currentTypeIndex] = currentType;
                            }
                            currentType = types[++currentTypeIndex];
                            currentType.FirstExplicitlyTargetedType = -1;
                            currentType.FirstTargetedType           = currentTargetIndex;
                            currentType.LastTargetedType            = currentTargetIndex;
                        }
                        else
                        {
                            currentType.LastTargetedType += 1;
                        }
                        targetedTypes[currentTargetIndex].BindType(currentTypeIndex, currentTarget);
                    }
                    if (!string.IsNullOrEmpty(currentTarget) && currentType.FirstExplicitlyTargetedType == -1)
                    {
                        currentType.FirstExplicitlyTargetedType = currentTargetIndex;
                    }
                }
                if (currentTypeIndex != -1)
                {
                    types[currentTypeIndex] = currentType;
                }

                // Now, associate indices in the sorted allIdentifiersList with each targeted type
                int nextIdentifier = 0;
                for (int i = 0; i < targetedTypesCount; ++i)
                {
                    currentTargetedType = targetedTypes[i];
                    string matchTypeName = types[currentTargetedType.TypeIndex].EnumTypeName;
                    string matchTarget   = currentTargetedType.Target;
                    currentTargetedType.FirstIdentifier   = nextIdentifier;
                    currentTargetedType.LastIdentifier    = nextIdentifier;
                    currentTargetedType.CurrentIdentifier = nextIdentifier;
                    Debug.Assert(allIdentifiers[nextIdentifier].IsDefaultIdentifier && allIdentifiers[nextIdentifier].EnumTypeName == matchTypeName, "No default snippets identifier for " + matchTypeName);
                    ++nextIdentifier;
                    bool matchedCurrent = currentIdentifiers == null;
                    for (; nextIdentifier < allIdentifiersCount; ++nextIdentifier)
                    {
                        if (allIdentifiers[nextIdentifier].Target != matchTarget ||
                            allIdentifiers[nextIdentifier].EnumTypeName != matchTypeName)
                        {
                            break;
                        }
                        currentTargetedType.LastIdentifier = nextIdentifier;
                        if (!matchedCurrent)
                        {
                            if (Array.IndexOf <VerbalizationSnippetsIdentifier>(currentIdentifiers, allIdentifiers[nextIdentifier]) >= 0)
                            {
                                currentTargetedType.CurrentIdentifier = nextIdentifier;
                                matchedCurrent = true;
                            }
                        }
                    }
                    targetedTypes[i] = currentTargetedType;
                }
                myTargetedTypes = targetedTypes;
                myTypes         = types;
                myIdentifiers   = allIdentifiers;
                if (languageFormatString != null)
                {
                    myItemStrings = new string[allIdentifiersCount];
                }
            }
 /// <summary>
 /// Translate the tree settings to a useable value
 /// </summary>
 protected override object TranslateToValue(ITypeDescriptorContext context, object oldValue, ITree tree, int selectedRow, int selectedColumn)
 {
     return(VerbalizationSnippetsIdentifier.SaveIdentifiers(((ProviderBranch)tree.Root).CurrentIdentifiers));
 }