private void AddToNameCollection(ref NamePart singleName, ref List<NamePart> nameCollection, NamePart newNamePart, int index, bool collapseAdjacentName) { string newName = newNamePart; newName = newName.Trim(); NamePartOptions options = newNamePart.Options; int startNameCount = GetNameCount(ref singleName, ref nameCollection); int endNameCount; // Test for space separated and pattern based multi-part names if (newName.IndexOfAny(NameDelimiterArray) != -1) { string[] individualEntries = newName.Split(NameDelimiterArray, StringSplitOptions.RemoveEmptyEntries); // We don't know at this point if the names are single or will split further with // the next call. Test how many items are added by tracking the count at each stage. for (int i = 0; i < individualEntries.Length; ++i) { // Add each space separated name individually AddToNameCollection(ref singleName, ref nameCollection, new NamePart(individualEntries[i], options), index == -1 ? -1 : index + (i == 0 ? 0 : GetNameCount(ref singleName, ref nameCollection) - startNameCount), false); } endNameCount = GetNameCount(ref singleName, ref nameCollection); } else if (0 == (options & NamePartOptions.ExplicitCasing) && EmbeddedCapsOrNumberRegex.IsMatch(newName)) { Match match = SplitOnUpperRegex.Match(newName); int matchIndex = 0; while (match.Success) { // Using the match index as an increment is sufficient // because we know the names will not split further and // adjacent names will not collapse. GroupCollection groups = match.Groups; AddToNameCollection(ref singleName, ref nameCollection, new NamePart(match.Value, groups["TrailingUpper"].Success || groups["Numeric"].Success ? NamePartOptions.ExplicitCasing : NamePartOptions.None), index == -1 ? -1 : index + matchIndex, false); ++matchIndex; match = match.NextMatch(); } endNameCount = startNameCount + matchIndex; } else if (singleName.IsEmpty) { // We only have one name so far, so just use the string singleName = new NamePart(newName, options); endNameCount = 1; } else { // We need to now use the collection if (null == nameCollection) { nameCollection = new List<NamePart>(); // First add the previously added element nameCollection.Add(singleName); } if (index == -1) { nameCollection.Add(new NamePart(newName, options)); } else { nameCollection.Insert(index, new NamePart(newName, options)); } endNameCount = startNameCount + 1; } int newNameCount; if (collapseAdjacentName && 0 != (newNameCount = (endNameCount - startNameCount))) // A name was added { // Remove duplicate names, treating the multiple parts as a split // name as a single name. if (index == -1) { index = startNameCount; } NamePart firstPart; NamePart secondPart; if (newNameCount <= startNameCount) // There are sufficient adjacent names to collapse a single or multi-part name { // Check for preceding name matches on all parts of the name while (index >= newNameCount) { int i = 0; for (; i < newNameCount; ++i) { firstPart = nameCollection[index + i]; secondPart = nameCollection[index - newNameCount + i]; if (!((string)firstPart).Equals((string)secondPart, firstPart.ExplicitCasing || secondPart.ExplicitCasing ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) { break; } } if (i < newNameCount) { break; } nameCollection.RemoveRange(index, newNameCount); index -= newNameCount; endNameCount -= newNameCount; } // Check for following name matches on all parts of the name while ((endNameCount - (index + newNameCount)) >= newNameCount) { int i = 0; for (; i < newNameCount; ++i) { firstPart = nameCollection[index + i]; secondPart = nameCollection[index + newNameCount + i]; if (!((string)firstPart).Equals((string)secondPart, firstPart.ExplicitCasing || secondPart.ExplicitCasing ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) { break; } } if (i < newNameCount) { break; } nameCollection.RemoveRange(index + newNameCount, newNameCount); index -= newNameCount; endNameCount -= newNameCount; } } if (newNameCount != 1) { // Enhance the multi-part collapse semantics by checking the // leading and trailing name parts. // Compare the parts preceding the first word while (index > 0 && ((string)(firstPart = nameCollection[index])).Equals((string)(secondPart = nameCollection[index - 1]), firstPart.ExplicitCasing || secondPart.ExplicitCasing ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) { nameCollection.RemoveAt(index); --index; --endNameCount; } // Compare the parts following the last word while ((index + newNameCount) < endNameCount && ((string)(firstPart = nameCollection[index + newNameCount - 1])).Equals((string)(secondPart = nameCollection[index + newNameCount]), firstPart.ExplicitCasing || secondPart.ExplicitCasing ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) { nameCollection.RemoveAt(index + newNameCount - 1); --endNameCount; } } } }
private string DoFirstLetterCase(NamePart name, bool upper, TextInfo textInfo) { string nameValue = name; if (string.IsNullOrEmpty(nameValue)) { return nameValue; } char c = nameValue[0]; if (upper) { c = textInfo.ToUpper(c); } else { c = textInfo.ToLower(c); } if (nameValue.Length > 1) { nameValue = c.ToString() + nameValue.Substring(1); } else { nameValue = c.ToString(); } return nameValue; }
private void AddToNameCollection(ref NamePart singleName, ref List<NamePart> nameCollection, NamePart newNamePart) { AddToNameCollection(ref singleName, ref nameCollection, newNamePart, -1, true); }
private string DoFirstWordCasing(NamePart name, NameGeneratorCasingOption casing, TextInfo textInfo) { if (name.ExplicitCasing) return name; switch (casing) { case NameGeneratorCasingOption.Camel: return TestHasAdjacentUpperCase(name) ? (string)name : DoFirstLetterCase(name, false, textInfo); case NameGeneratorCasingOption.Pascal: return TestHasAdjacentUpperCase(name) ? (string)name : DoFirstLetterCase(name, true, textInfo); case NameGeneratorCasingOption.Lower: return TestHasAdjacentUpperCase(name) ? (string)name : textInfo.ToLower(name); case NameGeneratorCasingOption.Upper: return textInfo.ToUpper(name); } return null; }
/// <summary> /// Helper for ResolveRecognizedPhrases. Returns true is parent processing is complete. /// </summary> private bool TestResolvePhraseDataForCollection(RecognizedPhraseData phraseData, ref NamePart singleName, ref List<NamePart> nameCollection, int collectionIndex, NameGenerator generator) { Debug.Assert(nameCollection != null); string[] matchNames = phraseData.OriginalNames; int matchLength = matchNames.Length; int i = 0; int firstExplicitPart = -1; int explicitPartCount = 0; for (; i < matchLength; ++i) // Note the bound on this is already verified by RecognizedPhraseData.Populate { NamePart testPart = nameCollection[collectionIndex + i]; bool currentPartExplicit = 0 != (testPart.Options & NamePartOptions.ExplicitCasing); if (0 != string.Compare(testPart, matchNames[i], currentPartExplicit ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase) || (matchLength == 1 && 0 != (testPart.Options & NamePartOptions.ReplacementOfSelf))) { break; } if (currentPartExplicit && firstExplicitPart == -1) { ++explicitPartCount; firstExplicitPart = i; } } if (i == matchLength) { // We have a valid replacement, apply it and recurse string[] explicitlyCasedNames = null; string singleMatchName = (matchLength == 1) ? matchNames[0] : null; if (explicitPartCount != 0) { explicitlyCasedNames = new string[explicitPartCount]; int nextExplicitName = 0; for (int j = collectionIndex + firstExplicitPart; ; ++j) { NamePart testPart = nameCollection[j]; if (0 != (testPart.Options & NamePartOptions.ExplicitCasing)) { explicitlyCasedNames[nextExplicitName] = testPart; if (++nextExplicitName == explicitPartCount) { break; } } } if (explicitPartCount > 1) { Array.Sort<string>(explicitlyCasedNames, StringComparer.CurrentCulture); } } nameCollection.RemoveRange(collectionIndex, matchLength); int startingCollectionSize = nameCollection.Count; string[] replacements = phraseData.ReplacementNames; for (i = 0; i < replacements.Length; ++i) { // Recognized phrases do not record casing priority and phrases are // generally treated as case insensitive. However, if any replacement // word exactly matches an explicitly cased word in the original names // then case the replacement as well. NamePartOptions options = NamePartOptions.None; string replacement = replacements[i]; if (explicitlyCasedNames != null && 0 <= Array.BinarySearch<string>(explicitlyCasedNames, replacement, StringComparer.CurrentCulture)) { options |= NamePartOptions.ExplicitCasing; if (matchLength == 1) { options |= NamePartOptions.ReplacementOfSelf; } } else if (singleMatchName != null && 0 == string.Compare(singleMatchName, replacement, StringComparison.CurrentCultureIgnoreCase)) { options |= NamePartOptions.ExplicitCasing; } AddToNameCollection(ref singleName, ref nameCollection, new NamePart(replacement, options), collectionIndex + nameCollection.Count - startingCollectionSize, true); } ResolveRecognizedPhrases(ref singleName, ref nameCollection, generator); return true; } return false; }
private void ResolveRecognizedPhrases(ref NamePart singleName, ref List<NamePart> nameCollection, NameGenerator generator) { ORMModel model = ContextModel; if (model != null) { if (nameCollection != null) { int nameCount = nameCollection.Count; int remainingParts = nameCount; for (int i = 0; i < nameCount; ++i, --remainingParts) { // For each part, collection possible replacement phrases beginning with that name NamePart currentPart = nameCollection[i]; RecognizedPhraseData singlePhrase = new RecognizedPhraseData(); List<RecognizedPhraseData> phraseList = null; bool possibleReplacement = false; foreach (NameAlias alias in model.GetRecognizedPhrasesStartingWith(currentPart, generator)) { RecognizedPhraseData phraseData; if (RecognizedPhraseData.Populate(alias, remainingParts, out phraseData)) { if (phraseList == null) { possibleReplacement = true; if (singlePhrase.IsEmpty) { singlePhrase = phraseData; } else { phraseList = new List<RecognizedPhraseData>(); phraseList.Add(singlePhrase); phraseList.Add(phraseData); singlePhrase = new RecognizedPhraseData(); } } else { phraseList.Add(phraseData); } } } // If we have possible replacements, then look farther to see // if the multi-part phrases match. Start by searching the longest // match possible. if (possibleReplacement) { if (phraseList != null) { phraseList.Sort(delegate(RecognizedPhraseData left, RecognizedPhraseData right) { return right.OriginalNames.Length.CompareTo(left.OriginalNames.Length); }); int phraseCount = phraseList.Count; for (int j = 0; j < phraseCount; ++j) { if (TestResolvePhraseDataForCollection(phraseList[j], ref singleName, ref nameCollection, i, generator)) { return; } } } else { if (TestResolvePhraseDataForCollection(singlePhrase, ref singleName, ref nameCollection, i, generator)) { return; } } } } } else if (!singleName.IsEmpty) { LocatedElement element = model.RecognizedPhrasesDictionary.GetElement(singleName); RecognizedPhrase phrase; NameAlias alias; if (null != (phrase = element.SingleElement as RecognizedPhrase) && null != (alias = generator.FindMatchingAlias(phrase.AbbreviationCollection))) { RecognizedPhraseData phraseData; if (RecognizedPhraseData.Populate(alias, 1, out phraseData)) { string[] replacements = phraseData.ReplacementNames; int replacementLength = replacements.Length; NamePart startingPart = singleName; singleName = new NamePart(); if (replacementLength == 0) { // Highly unusual, but possible with collapsing phrases and omitted readings singleName = new NamePart(); } else { string testForEqual = singleName; bool caseIfEqual = 0 != (singleName.Options & NamePartOptions.ExplicitCasing); singleName = new NamePart(); if (replacementLength == 1) { string replacement = replacements[0]; NamePartOptions options = NamePartOptions.None; if ((caseIfEqual && 0 == string.Compare(testForEqual, replacement, StringComparison.CurrentCulture)) || (0 == string.Compare(testForEqual, replacement, StringComparison.CurrentCultureIgnoreCase))) { // Single replacement for same string return; } AddToNameCollection(ref singleName, ref nameCollection, new NamePart(replacement, options)); } else { for (int i = 0; i < replacementLength; ++i) { string replacement = replacements[i]; NamePartOptions options = NamePartOptions.None; if (caseIfEqual && 0 == string.Compare(testForEqual, replacement, StringComparison.CurrentCulture)) { options |= NamePartOptions.ExplicitCasing | NamePartOptions.ReplacementOfSelf; } else if (0 == string.Compare(testForEqual, replacement, StringComparison.CurrentCultureIgnoreCase)) { options |= NamePartOptions.ReplacementOfSelf; } AddToNameCollection(ref singleName, ref nameCollection, new NamePart(replacement, options)); } } ResolveRecognizedPhrases(ref singleName, ref nameCollection, generator); } return; } } } } }
private string GetFinalName(NamePart singleName, List<NamePart> nameCollection, NameGenerator generator) { ResolveRecognizedPhrases(ref singleName, ref nameCollection, generator); NameGeneratorCasingOption casing = generator.CasingOption; string space = GetSpacingReplacement(generator); string finalName; if (null == nameCollection) { if (singleName.IsEmpty) { return ""; } if (casing == NameGeneratorCasingOption.None) { finalName = singleName; } else { finalName = DoFirstWordCasing(singleName, casing, CultureInfo.CurrentCulture.TextInfo); } } else { TextInfo textInfo = CultureInfo.CurrentCulture.TextInfo; string name; if (casing == NameGeneratorCasingOption.None) { name = nameCollection[0]; } else { name = DoFirstWordCasing(nameCollection[0], casing, textInfo); } //we already know there are at least two name entries, so use a string builder StringBuilder builder = new StringBuilder(name); //we already have the first entry, so mark camel as pascal NameGeneratorCasingOption tempCasing = casing; if (tempCasing == NameGeneratorCasingOption.Camel) { tempCasing = NameGeneratorCasingOption.Pascal; } //add each entry with proper spaces and casing int count = nameCollection.Count; for (int i = 1; i < count; ++i) { builder.Append(space); if (casing == NameGeneratorCasingOption.None) { name = nameCollection[i]; } else { name = DoFirstWordCasing(nameCollection[i], tempCasing, textInfo); } builder.Append(name); } finalName = builder.ToString(); } return finalName; }
private static int GetNameCount(ref NamePart singleName, ref List<NamePart> nameCollection) { if (singleName.IsEmpty) { return 0; } else if (nameCollection == null) { return 1; } return nameCollection.Count; }