/// ------------------------------------------------------------------------------------
 public WordCacheEntry(RecordCacheEntry recEntry, string phoneticFieldName)
 {
     RecordEntry    = recEntry;
     WordIndex      = 0;
     _phoneticValue = new FieldValue(phoneticFieldName);
     _fieldValues[phoneticFieldName] = _phoneticValue;
 }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        ///
        /// \tx nimeolewa.
        /// \mb ni-  me - oa    -le  -wa
        /// \ge 1S-  PF - marry -DER -PASS
        /// \ps pro- tns- v     -sfx -sfx
        ///
        /// </summary>
        /// ------------------------------------------------------------------------------------
        private static void ParseEntryAsInterlinear(RecordCacheEntry entry)
        {
            string firstInterlinearLine = entry[entry.FirstInterlinearField];

            if (string.IsNullOrEmpty(firstInterlinearLine))
            {
                return;
            }

            // Get the width of each interlinear column.
            var colWidths = GetInterlinearColumnWidths(firstInterlinearLine);

            // Store the unparsed interlinear lines in a collection of strings, then remove
            // those lines from the record cache entry so they no longer take up space.
            var unparsedLines = entry.InterlinearFields.ToDictionary(f => f, f => entry[f]);

            foreach (var field in entry.InterlinearFields)
            {
                entry.SetValue(field, null);
            }

            // Now parse each interlinear line.
            int startIndex = 0;
            int wordIndex  = 0;

            foreach (int width in colWidths)
            {
                var wordEntry = new WordCacheEntry(entry, wordIndex++);

                foreach (var line in unparsedLines.Where(l => l.Value != null && startIndex < l.Value.Length))
                {
                    wordEntry[line.Key] = (startIndex + width > line.Value.Length ?
                                           line.Value.Substring(startIndex) : line.Value.Substring(startIndex, width)).Trim();

                    if (line.Key == entry.FirstInterlinearField)
                    {
                        wordEntry.SetFieldAsFirstLineInterlinear(line.Key);
                    }
                    else
                    {
                        wordEntry.SetFieldAsSubordinateInterlinear(line.Key);
                    }
                }

                entry.WordEntries.Add(wordEntry);
                startIndex += width;
            }
        }
        /// ------------------------------------------------------------------------------------
        public WordCacheEntry(RecordCacheEntry recEntry, int wordIndex)
        {
            RecordEntry = recEntry;
            WordIndex   = wordIndex;

            if (recEntry == null)
            {
                return;
            }

            var mapping = recEntry.DataSource.FieldMappings
                          .SingleOrDefault(m => m.Field != null && m.Field.Type == FieldType.Phonetic);

            if (mapping != null)
            {
                _phoneticValue = new FieldValue(mapping.Field.Name);
                _fieldValues[mapping.Field.Name] = _phoneticValue;
            }
        }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Parses a non interlinear field (if necessary) and saves the field contents in one
        /// or more word cache entries. Parsing will depend on the data source's parse type
        /// and the field being parsed.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        private static void ParseSingleFieldInEntry(RecordCacheEntry entry, PaField field)
        {
            entry.NeedsParsing = false;
            string unparsedData = entry[field.Name];

            if (string.IsNullOrEmpty(unparsedData))
            {
                return;
            }

            // If we're not dealing with the phonetic field then check if our parsing type is
            // only phonetic or none at all. If either casecase then do nothing which will cause
            // any reference to the word cache entry's value for the field to defer to the
            // value that's stored in the word cache entry's owning record entry.
            if (field.Type != FieldType.Phonetic &&
                (entry.DataSource.ParseType == DataSourceParseType.PhoneticOnly ||
                 entry.DataSource.ParseType == DataSourceParseType.None))
            {
                return;
            }

            // By this time we know we're dealing with one of three conditions: 1) the
            // field is phonetic or 2) the field should be parsed or 3) both 1 and
            // 2. When the field should be parsed then split it into individual words.
            string[] split = (entry.DataSource.ParseType == DataSourceParseType.None ?
                              new[] { unparsedData } : unparsedData.Split(App.BreakChars.ToCharArray(),
                                                                          StringSplitOptions.RemoveEmptyEntries));

            for (int i = 0; i < split.Length; i++)
            {
                // Expand the capacity for more word entries if necessary.
                if (i == entry.WordEntries.Count)
                {
                    entry.WordEntries.Add(new WordCacheEntry(entry, i));
                }

                entry.WordEntries[i].SetValue(field.Name, split[i]);
            }
        }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Parses a single record, going through its fields and parsing them into individual
        /// word entries.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        public void ParseEntry(RecordCacheEntry entry)
        {
            TmpRecCacheAddAction(entry.Id, entry[m_phoneticFieldName]);
            entry.WordEntries = new List <WordCacheEntry>();

            // Parse interlinear fields first, if there are any.
            if (entry.HasInterlinearData)
            {
                ParseEntryAsInterlinear(entry);
            }

            var eticField = entry.Project.GetPhoneticField();

//			var eticMapping = entry.DataSource.FieldMappings.Single(m => m.Field.Name == eticField.Name);

            // If we didn't parse any interlinear fields or the phonetic wasn't among
            // them, make sure it gets parsed before any other non interlinear fields.
            //if (eticMapping.IsParsed && !entry.GetIsInterlinearField(eticField.Name))
            //    ParseSingleFieldInEntry(entry, eticField);

            // I've commented out the check above because it doesn't seem right that the
            // phonetic mapping's IsParsed can be false. Therefore, I will always parse
            // it. I hesitate to delete the commented out code in case I'm overlooking
            // something.
            if (!entry.GetIsInterlinearField(eticField.Name))
            {
                ParseSingleFieldInEntry(entry, eticField);
            }

            // Parse all the non phonetic, non interlinear fields.
            foreach (var mapping in entry.DataSource.FieldMappings.Where(m => m.IsParsed &&
                                                                         m.Field.Type != FieldType.Phonetic && !entry.GetIsInterlinearField(m.Field.Name)))
            {
                ParseSingleFieldInEntry(entry, mapping.Field);
            }
        }
 /// ------------------------------------------------------------------------------------
 public WordCacheEntry(RecordCacheEntry recEntry) : this(recEntry, 0)
 {
 }