/// <summary>
        /// Adds a new named entity instance into the list, while avoid the duplicated ones.
        /// </summary>
        /// <param name="entity">The named entity instance to add.</param>
        /// <returns>Whether succeeded added.</returns>
        public bool AddNamedEntity(ScriptNamedEntity entity)
        {
            bool existed = false;
            foreach (var item in NamedEntities)
            {
                if (item.Equals(entity))
                {
                    existed = true;
                    break;
                }
            }

            if (!existed)
            {
                NamedEntities.Add(entity);
                _namedEntities = NamedEntities.SortBy(e => e.StartIndex).ToList();
            }

            return !existed;
        }
        /// <summary>
        /// Converts one word instance to script named entity.
        /// </summary>
        /// <param name="word">The word instance to convert.</param>
        /// <returns>The converted script named entity instance.</returns>
        private ScriptNamedEntity ToScriptNamedEntity(ScriptWord word)
        {
            Helper.ThrowIfNull(word);
            if (string.IsNullOrEmpty(word.NamedEntityTypeString))
            {
                throw new InvalidDataException(Helper.NeutralFormat(
                    "The type of the named entity [{0}] should not be empty.", word.NamedEntityTypeString));
            }

            ScriptNamedEntity entity = new ScriptNamedEntity();

            entity.Text = word.Grapheme;
            entity.PosString = word.PosString;
            entity.Type = word.NamedEntityTypeString;

            entity.Start = word.SubWords[0];
            entity.End = word.SubWords[word.SubWords.Count - 1];

            return entity;
        }
        /// <summary>
        /// Converts one named entity to script word instance.
        /// </summary>
        /// <param name="entity">The named entity instance to convert.</param>
        /// <returns>The converted word instance.</returns>
        private ScriptWord ToScriptWord(ScriptNamedEntity entity)
        {
            Helper.ThrowIfNull(entity);
            if (string.IsNullOrEmpty(entity.Type))
            {
                throw new InvalidDataException(Helper.NeutralFormat(
                    "The type of the named entity [{0}] should not be empty.", entity.Text));
            }

            ScriptWord word = new ScriptWord(entity.Start.Language);

            word.Break = entity.End.Break;

            word.Grapheme = entity.Text;
            word.PosString = entity.PosString;

            if (word.PosString == ScriptNamedEntity.DefaultEmptyPosString)
            {
                word.PosString = ScriptNamedEntity.DefaultEntityPosString;
            }

            word.NamedEntityTypeString = entity.Type;
            word.Sentence = entity.Start.Sentence;

            word.SubWords = new Collection<ScriptWord>();
            StringBuilder pronunciation = new StringBuilder();
            for (int i = Words.IndexOf(entity.Start); i <= Words.IndexOf(entity.End); i++)
            {
                word.SubWords.Add(Words[i]);
                if (!string.IsNullOrEmpty(Words[i].Pronunciation))
                {
                    if (pronunciation.Length != 0)
                    {
                        pronunciation.AppendFormat(" {0} ", Pronunciation.WordPronBoundaryString);
                    }

                    pronunciation.Append(Words[i].Pronunciation);
                }
            }

            word.Pronunciation = pronunciation.ToString();

            return word;
        }
        /// <summary>
        /// Load one script named entity from the xml text reader.
        /// </summary>
        /// <param name="reader">The XML reader instance to read data from.</param>
        /// <param name="sentence">Script sentence.</param>
        /// <param name="scriptContentController">ContentControler.</param>
        /// <returns>ScriptNamedEntity instance that read.</returns>
        public static ScriptNamedEntity LoadNamedEntity(XmlTextReader reader,
            ScriptSentence sentence, ContentControler scriptContentController)
        {
            Debug.Assert(reader != null);
            Debug.Assert(scriptContentController != null);
            ScriptNamedEntity entity = new ScriptNamedEntity();
            entity.Type = reader.GetAttribute("type");
            entity.Text = reader.GetAttribute("v");
            string pos = reader.GetAttribute("pos");
            if (!string.IsNullOrEmpty(pos))
            {
                entity.PosString = pos;
            }

            Debug.Assert(sentence.Words.Count > 0);
            int startIndex = int.Parse(reader.GetAttribute("s"), CultureInfo.InvariantCulture);
            int endIndex = int.Parse(reader.GetAttribute("e"), CultureInfo.InvariantCulture);

            Collection<ScriptWord> graphemeWords = sentence.TextWords;
            if (startIndex < 0 && startIndex >= graphemeWords.Count)
            {
                throw new InvalidDataException(Helper.NeutralFormat(
                    "Invalid start index for sentence [{0}] : [{1}]",
                    sentence.ScriptItem.GetSentenceId(sentence), startIndex));
            }

            entity.Start = graphemeWords[startIndex];

            if (endIndex < 0 || endIndex >= graphemeWords.Count)
            {
                throw new InvalidDataException(Helper.NeutralFormat(
                    "Invalid end index for sentence [{0}] : [{1}]",
                    sentence.ScriptItem.GetSentenceId(sentence), endIndex));
            }

            entity.End = graphemeWords[endIndex];
            return entity;
        }