/// ------------------------------------------------------------------------------------
        /// <summary>
        /// Populates the key terms table.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        private void PopulateKeyTermsTable(IEnumerable <IKeyTerm> keyTerms, KeyTermRules rules)
        {
            Dictionary <string, KeyTermRule> ktRules = new Dictionary <string, KeyTermRule>();

            if (rules != null)
            {
                foreach (KeyTermRule keyTermRule in rules.Items.Where(keyTermRule => !String.IsNullOrEmpty(keyTermRule.id)))
                {
                    ktRules[keyTermRule.id] = keyTermRule;
                }
            }

            KeyTermMatchBuilder matchBuilder;

            foreach (IKeyTerm keyTerm in keyTerms)
            {
                matchBuilder = new KeyTermMatchBuilder(keyTerm, ktRules);

                foreach (KeyTermMatch matcher in matchBuilder.Matches)
                {
                    if (!matcher.Words.Any())
                    {
                        continue;
                    }

                    List <KeyTermMatch> foundMatchers;
                    Word firstWord = matcher.Words.First();
                    if (!m_keyTermsTable.TryGetValue(firstWord, out foundMatchers))
                    {
                        m_keyTermsTable[firstWord] = foundMatchers = new List <KeyTermMatch>();
                    }

                    KeyTermMatch existingMatcher = foundMatchers.FirstOrDefault(m => m.Equals(matcher));
                    if (existingMatcher == null)
                    {
                        foundMatchers.Add(matcher);
                    }
                    else
                    {
                        existingMatcher.AddTerm(keyTerm);
                    }
                }
            }
        }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Initializes a new instance of the <see cref="PhraseTranslationHelper"/> class.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        public PhraseTranslationHelper(IEnumerable <TranslatablePhrase> phrases,
                                       IEnumerable <IKeyTerm> keyTerms, KeyTermRules keyTermRules,
                                       IEnumerable <Substitution> phrasesToIgnore)
        {
            TranslatablePhrase.s_helper = this;

            m_keyTermsTable = new Dictionary <Word, List <KeyTermMatch> >(keyTerms.Count());
            PopulateKeyTermsTable(keyTerms, keyTermRules);

            m_phraseSubstitutions = new Dictionary <Regex, string>(phrasesToIgnore.Count());
            foreach (Substitution substitutePhrase in phrasesToIgnore)
            {
                m_phraseSubstitutions[substitutePhrase.RegEx] = substitutePhrase.RegExReplacementString;
            }

            m_partsTable = new SortedDictionary <int, Dictionary <Word, List <Part> > >();
            foreach (TranslatablePhrase phrase in phrases.Where(p => !string.IsNullOrEmpty(p.PhraseToDisplayInUI)))
            {
                if (!phrase.IsExcluded)
                {
                    PhraseParser parser = new PhraseParser(m_keyTermsTable, m_phraseSubstitutions, phrase, GetOrCreatePart);
                    foreach (IPhrasePart part in parser.Parse())
                    {
                        phrase.m_parts.Add(part);
                    }
                }
                m_phrases.Add(phrase);
                if (phrase.Category == -1)
                {
                    m_categories[phrase.SequenceNumber] = phrase;
                }
            }

            for (int wordCount = m_partsTable.Keys.Max(); wordCount > 1; wordCount--)
            {
                Dictionary <Word, List <Part> > partsTable;
                if (!m_partsTable.TryGetValue(wordCount, out partsTable))
                {
                    continue;
                }

                List <Part> partsToDelete = new List <Part>();

                foreach (KeyValuePair <Word, List <Part> > phrasePartPair in partsTable)              // REVIEW: problem: won't be able to add a new part that starts with this word
                {
                    foreach (Part part in phrasePartPair.Value)
                    {
                        if (part.OwningPhrases.Count() != 1)
                        {
                            continue;
                        }

                        // Look to see if some other part is a sub-phrase of this part.
                        SubPhraseMatch match = FindSubPhraseMatch(part);
                        if (match != null)
                        {
                            TranslatablePhrase owningPhraseOfPart = part.OwningPhrases.First();
                            int iPart = owningPhraseOfPart.m_parts.IndexOf(part);
                            // Deal with any preceding remainder
                            if (match.StartIndex > 0)
                            {
                                Part preceedingPart = GetOrCreatePart(part.GetSubWords(0, match.StartIndex), owningPhraseOfPart, wordCount);
                                owningPhraseOfPart.m_parts.Insert(iPart++, preceedingPart);
                            }
                            match.Part.AddOwningPhrase(owningPhraseOfPart);
                            owningPhraseOfPart.m_parts[iPart++] = match.Part;
                            // Deal with any following remainder
                            // Breaks this part at the given position because an existing part was found to be a
                            // substring of this part. Any text before the part being excluded will be broken off
                            // as a new part and returned. Any text following the part being excluded will be kept
                            // as this part's contents.
                            if (match.StartIndex + match.Part.m_words.Count < part.m_words.Count)
                            {
                                Part followingPart = GetOrCreatePart(part.GetSubWords(match.StartIndex + match.Part.m_words.Count), owningPhraseOfPart, wordCount);
                                owningPhraseOfPart.m_parts.Insert(iPart, followingPart);
                            }
                            partsToDelete.Add(part);
                        }
                    }
                }
                foreach (Part partToDelete in partsToDelete)
                {
                    partsTable[partToDelete.m_words[0]].Remove(partToDelete);
                }
            }
            m_filteredPhrases = m_phrases;
        }
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Adds the mocked key term.
		/// </summary>
		/// ------------------------------------------------------------------------------------
		private IKeyTerm AddMockedKeyTerm(string term, string bestRendering, string[] otherRenderings,
			params int[] occurences)
		{
			IKeyTerm mockedKt = KeyTermMatchBuilderTests.AddMockedKeyTerm(term, occurences);
			if (bestRendering != null)
			{
				otherRenderings = (otherRenderings == null) ? new [] { bestRendering } :
					(new [] { bestRendering }).Concat(otherRenderings).ToArray();
				mockedKt.Stub(kt => kt.BestRendering).Return(bestRendering);
			}
			mockedKt.Stub(kt => kt.Renderings).Return(otherRenderings);

			if (occurences.Length > 0)
			{
				if (m_keyTermRules == null)
				{
					m_keyTermRules = new KeyTermRules();
					m_keyTermRules.Items = new List<KeyTermRule>();
				}
				KeyTermRule rule = new KeyTermRule();
				rule.id = term;
				rule.Rule = KeyTermRule.RuleType.MatchForRefOnly;
				m_keyTermRules.Items.Add(rule);
			}
			m_dummyKtList.Add(mockedKt);
			return mockedKt;
		}
		public void Setup()
		{
			m_dummyKtList = new List<IKeyTerm>();
			m_keyTermRules = null;
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Initializes a new instance of the <see cref="PhraseTranslationHelper"/> class.
		/// </summary>
		/// ------------------------------------------------------------------------------------
		public PhraseTranslationHelper(IEnumerable<TranslatablePhrase> phrases,
			IEnumerable<IKeyTerm> keyTerms, KeyTermRules keyTermRules,
			IEnumerable<Substitution> phrasesToIgnore)
		{
			TranslatablePhrase.s_helper = this;

			m_keyTermsTable = new Dictionary<Word, List<KeyTermMatch>>(keyTerms.Count());
			PopulateKeyTermsTable(keyTerms, keyTermRules);

			m_phraseSubstitutions = new Dictionary<Regex, string>(phrasesToIgnore.Count());
			foreach (Substitution substitutePhrase in phrasesToIgnore)
				m_phraseSubstitutions[substitutePhrase.RegEx] = substitutePhrase.RegExReplacementString;

			m_partsTable = new SortedDictionary<int, Dictionary<Word, List<Part>>>();
			foreach (TranslatablePhrase phrase in phrases.Where(p => !string.IsNullOrEmpty(p.PhraseToDisplayInUI)))
			{
				if (!phrase.IsExcluded)
				{
					PhraseParser parser = new PhraseParser(m_keyTermsTable, m_phraseSubstitutions, phrase, GetOrCreatePart);
					foreach (IPhrasePart part in parser.Parse())
						phrase.m_parts.Add(part);
				}
				m_phrases.Add(phrase);
				if (phrase.Category == -1)
					m_categories[phrase.SequenceNumber] = phrase;
			}

			for (int wordCount = m_partsTable.Keys.Max(); wordCount > 1; wordCount--)
			{
				Dictionary<Word, List<Part>> partsTable;
				if (!m_partsTable.TryGetValue(wordCount, out partsTable))
					continue;

				List<Part> partsToDelete = new List<Part>();

				foreach (KeyValuePair<Word, List<Part>> phrasePartPair in partsTable) // REVIEW: problem: won't be able to add a new part that starts with this word
				{
					foreach (Part part in phrasePartPair.Value)
					{
						if (part.OwningPhrases.Count() != 1)
							continue;

						// Look to see if some other part is a sub-phrase of this part.
						SubPhraseMatch match = FindSubPhraseMatch(part);
						if (match != null)
						{
							TranslatablePhrase owningPhraseOfPart = part.OwningPhrases.First();
							int iPart = owningPhraseOfPart.m_parts.IndexOf(part);
							// Deal with any preceding remainder
							if (match.StartIndex > 0)
							{
								Part preceedingPart = GetOrCreatePart(part.GetSubWords(0, match.StartIndex), owningPhraseOfPart, wordCount);
								owningPhraseOfPart.m_parts.Insert(iPart++, preceedingPart);
							}
							match.Part.AddOwningPhrase(owningPhraseOfPart);
							owningPhraseOfPart.m_parts[iPart++] = match.Part;
							// Deal with any following remainder
							// Breaks this part at the given position because an existing part was found to be a
							// substring of this part. Any text before the part being excluded will be broken off
							// as a new part and returned. Any text following the part being excluded will be kept
							// as this part's contents.
							if (match.StartIndex + match.Part.m_words.Count < part.m_words.Count)
							{
								Part followingPart = GetOrCreatePart(part.GetSubWords(match.StartIndex + match.Part.m_words.Count), owningPhraseOfPart, wordCount);
								owningPhraseOfPart.m_parts.Insert(iPart, followingPart);
							}
							partsToDelete.Add(part);
						}
					}
				}
				foreach (Part partToDelete in partsToDelete)
				{
					partsTable[partToDelete.m_words[0]].Remove(partToDelete);
				}
			}
			m_filteredPhrases = m_phrases;
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Populates the key terms table.
		/// </summary>
		/// ------------------------------------------------------------------------------------
		private void PopulateKeyTermsTable(IEnumerable<IKeyTerm> keyTerms, KeyTermRules rules)
		{
			Dictionary<string, KeyTermRule> ktRules = new Dictionary<string, KeyTermRule>();
			if (rules != null)
			{
				foreach (KeyTermRule keyTermRule in rules.Items.Where(keyTermRule => !String.IsNullOrEmpty(keyTermRule.id)))
					ktRules[keyTermRule.id] = keyTermRule;
			}

			KeyTermMatchBuilder matchBuilder;

			foreach (IKeyTerm keyTerm in keyTerms)
			{
				matchBuilder = new KeyTermMatchBuilder(keyTerm, ktRules);

				foreach (KeyTermMatch matcher in matchBuilder.Matches)
				{
					if (!matcher.Words.Any())
						continue;

					List<KeyTermMatch> foundMatchers;
					Word firstWord = matcher.Words.First();
					if (!m_keyTermsTable.TryGetValue(firstWord, out foundMatchers))
						m_keyTermsTable[firstWord] = foundMatchers = new List<KeyTermMatch>();

					KeyTermMatch existingMatcher = foundMatchers.FirstOrDefault(m => m.Equals(matcher));
					if (existingMatcher == null)
						foundMatchers.Add(matcher);
					else
						existingMatcher.AddTerm(keyTerm);
				}
			}
		}