예제 #1
0
        public static ParseResult <Image> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            var image = new Image();

            image.ID = ParserHelper.ParseID(first.GetFirstItem()).ToString();

            var initialLevel = first.Level;

            GedcomLine          line = default;
            ReadOnlySpan <char> currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()) != null)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == first.Level)
                {
                    break;
                }

                var splitLine = line.GetFirstItem();
                if (ParserHelper.Equals(splitLine, "TITL"))
                {
                    var title = line.GetLineContent(4);
                    image.Title = title.ToString();
                }
                else if (ParserHelper.Equals(splitLine, "FILE"))
                {
                    var filePath = line.GetLineContent(4);
                    image.FilePath = filePath.ToString();
                }
            }

            return(ParseResult.Create(image, line));
        }
예제 #2
0
        public static ParseResult <GedcomEvent> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            GedcomEvent gedcomEvent = new GedcomEvent();

            var initialLevel = first.Level;

            GedcomLine          line = default;
            ReadOnlySpan <char> currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()).Length > 0)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level <= first.Level)
                {
                    break;
                }

                ReadOnlySpan <char> tag = line.GetFirstItem();
                if (ParserHelper.Equals(tag, "DATE"))
                {
                    // If checks we're parsing actual date and not
                    // CREA or CHAN tags
                    // TODO: should actually put CREA and CHAN into different parser
                    if (line.Level == initialLevel + 1)
                    {
                        var dateContent = line.GetLineContent(4);
                        gedcomEvent.Date = dateContent.Length == 0 ? null : dateContent.ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "PLAC"))
                {
                    // If checks we're parsing actual date and not
                    // CREA or CHAN tags
                    // TODO: should actually put CREA and CHAN into different parser
                    if (line.Level == initialLevel + 1)
                    {
                        var placContent = line.GetLineContent(4);
                        gedcomEvent.Location = placContent.Length == 0 ? null : placContent.ToString();
                    }
                }
            }

            return(ParseResult.Create(gedcomEvent, line));
        }
예제 #3
0
        public static ParseResult <GedcomEvent> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            GedcomEvent gedcomEvent = new GedcomEvent();

            var initialLevel = first.Level;

            GedcomLine line = default;
            string     currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()) != null)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level <= first.Level)
                {
                    break;
                }

                switch (line.GetTagOrRef())
                {
                case "DATE":
                    // If checks we're parsing actual date and not
                    // CREA or CHAN tags
                    // TODO: should actually put CREA and CHAN into different parser
                    if (line.Level == initialLevel + 1)
                    {
                        gedcomEvent.Date = line.GetLineContent();
                    }
                    break;

                case "PLAC":
                    // If checks we're parsing actual date and not
                    // CREA or CHAN tags
                    // TODO: should actually put CREA and CHAN into different parser
                    if (line.Level == initialLevel + 1)
                    {
                        gedcomEvent.Location = line.GetLineContent();
                    }
                    break;
                }
            }

            return(ParseResult.Create(gedcomEvent, line));
        }
예제 #4
0
        public static ParseResult <Note> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            string id   = ParserHelper.ParseID(first.GetFirstItem()).ToString();
            string text = null;

            var initialLevel = first.Level;

            GedcomLine          line = default;
            ReadOnlySpan <char> currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()).Length > 0)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == first.Level)
                {
                    break;
                }

                ReadOnlySpan <char> tag = line.GetFirstItem();

                if (ParserHelper.Equals(tag, "CONT"))
                {
                    string contText = line.GetLineContent(4).ToString();
                    text += Environment.NewLine + contText;
                }
                else if (ParserHelper.Equals(tag, "CONC"))
                {
                    // TODO: is GenesReunited maintaining the trailing space?
                    // If so, is this correct?
                    string concText = line.GetLineContent(4).ToString();
                    text += concText;
                }
            }

            return(ParseResult.Create(new Note(id, text), line));
        }
예제 #5
0
        public static ParseResult <Note> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            string id   = ParserHelper.ParseID(first.GetTagOrRef());
            string text = null;

            var initialLevel = first.Level;

            GedcomLine line = default;
            string     currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()) != null)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == first.Level)
                {
                    break;
                }

                switch (line.GetTagOrRef())
                {
                case "CONT":
                    string contText = line.GetLineContent();
                    text += Environment.NewLine + contText;
                    break;

                case "CONC":
                    // TODO: is GenesReunited maintaining the trailing space?
                    // If so, is this correct?
                    string concText = line.GetLineContent();
                    text += concText;
                    break;
                }
            }

            return(ParseResult.Create(new Note(id, text), line));
        }
예제 #6
0
        public static ParseResult <Image> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            var image = new Image();

            image.ID = ParserHelper.ParseID(first.GetTagOrRef());

            var initialLevel = first.Level;

            GedcomLine line = default;
            string     currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()) != null)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == first.Level)
                {
                    break;
                }

                switch (line.GetTagOrRef())
                {
                case "TITL":
                    var title = line.GetLineContent();
                    image.Title = title;
                    break;

                case "FILE":
                    var filePath = line.GetLineContent();
                    image.FilePath = filePath;
                    break;
                }
            }

            return(ParseResult.Create(image, line));
        }
예제 #7
0
        public static ParseResult <GedcomHeader> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            CurrentLevel currentLevel = CurrentLevel.None;

            var header = new GedcomHeader();

            GedcomLine line = default;
            string     currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()) != null)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == 0)
                {
                    break;
                }

                if (line.Level == 1)
                {
                    switch (line.GetTagOrRef())
                    {
                    case "SOUR":
                        currentLevel = CurrentLevel.Sour;
                        break;

                    case "GEDC":
                        currentLevel = CurrentLevel.Gedc;
                        break;

                    case "CHAR":
                        header.GedcomCharacterSet = line.GetLineContent();
                        break;
                    }
                }
                else if (line.Level == 2)
                {
                    if (currentLevel == CurrentLevel.Sour)
                    {
                        switch (line.GetTagOrRef())
                        {
                        case "NAME":
                            header.SourceName = line.GetLineContent();
                            break;

                        case "VERS":
                            header.SourceVers = line.GetLineContent();
                            break;

                        case "CORP":
                            header.SourceCorp = line.GetLineContent();
                            break;
                        }
                    }
                    else if (currentLevel == CurrentLevel.Gedc)
                    {
                        if (line.GetTagOrRef() == "VERS")
                        {
                            header.GedcomVers = line.GetLineContent();
                        }
                    }
                }
            }

            return(ParseResult.Create(header, line));
        }
예제 #8
0
        public static ParseResult <Family> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            var family = new Family();

            family.ID = ParserHelper.ParseID(first.GetFirstItem()).ToString();

            bool inMarriage = false;

            var initialLevel = first.Level;

            GedcomLine          line = default;
            ReadOnlySpan <char> currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()).Length > 0)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == first.Level)
                {
                    break;
                }

                var tag = line.GetFirstItem();

                if (ParserHelper.Equals(tag, "MARR"))
                {
                    inMarriage = true;
                }
                else if (ParserHelper.Equals(tag, "DATE"))
                {
                    if (inMarriage) // TODO: should have MARR parser
                    {
                        var date = line.GetLineContent(4);
                        if (family.Marriage == null)
                        {
                            family.Marriage = new GedcomEvent();
                        }
                        family.Marriage.Date = date.ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "PLAC"))
                {
                    if (inMarriage) // Assume level + 1 is MARR
                    {
                        var place = line.GetLineContent(4);
                        if (family.Marriage == null)
                        {
                            family.Marriage = new GedcomEvent();
                        }
                        family.Marriage.Location = place.ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "HUSB"))
                {
                    // Ignore any husband and wife information in the middle of a marriage tag.
                    // Present for torture test files - and info redundant?
                    // can have e.g. "2 HUSB", with no additional info
                    var husbContent = line.GetLineContent(4);
                    if (husbContent.Length > 0)
                    {
                        family.HusbandID = ParserHelper.ParseID(husbContent).ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "WIFE"))
                {
                    var wifeContent = line.GetLineContent(4);
                    // Ignore any husband and wife information in the middle of a marriage tag.
                    // Present for torture test files - and info redundant?
                    // can have e.g. "2 HUSB", with no additional info
                    if (wifeContent.Length > 0)
                    {
                        family.WifeID = ParserHelper.ParseID(wifeContent).ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "CHIL"))
                {
                    var childContent = line.GetLineContent(4);
                    family.ChildIDs.Add(ParserHelper.ParseID(childContent).ToString());
                }
                else
                {
                    inMarriage = false;
                }
            }

            return(ParseResult.Create(family, line));
        }
예제 #9
0
        public static ParseResult <Individual> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            var individual = new Individual();

            individual.ID = ParserHelper.ParseID(first.GetTagOrRef());

            GedcomLine line = default;
            string     currentRawLine;
            var        newLine = false;

            while (true)
            {
                // TODO: this loop is really messy, as some of the parsing
                //  works on lines as retrieved from the parser, where some
                //  other iterations call onto a sub-parser which return a final
                //  line. Should split this logic
                if (!newLine)
                {
                    currentRawLine = lineProvider.ReadLine();
                    if (currentRawLine == null)
                    {
                        break;
                    }
                    line = ParserHelper.ParseLine(currentRawLine);
                }
                newLine = false;

                if (line.Level == 0)
                {
                    break;
                }

                switch (line.GetTagOrRef())
                {
                case "NAME":
                    if (individual.LastName != null || individual.FirstNames != null)
                    {
                        break;
                    }

                    ParseNames(line, individual);
                    break;

                case "SEX":
                    individual.Gender = (line.GetLineContent() == "M") ? Gender.Male : Gender.Female;
                    break;

                case "FAMS":
                    individual.FamilyIDSpouse = ParserHelper.ParseID(line.GetLineContent());
                    break;

                case "FAMC":
                    individual.FamilyIDChild = ParserHelper.ParseID(line.GetLineContent());
                    break;

                case "OBJE":
                    var objeContent = line.GetLineContent();
                    if (!string.IsNullOrEmpty(objeContent))     // TODO: test for this - currently don't support Images directly in Individual
                    // should fix this
                    {
                        individual.ImageID = ParserHelper.ParseID(objeContent);
                    }
                    break;

                case "NOTE":
                    string noteText = line.GetLineContent() ?? string.Empty;
                    // My be an empty NOTE tag, but will be concatenated onto

                    if (noteText.StartsWith("@"))
                    {
                        string noteId = ParserHelper.ParseID(noteText);
                        individual.NoteID = noteId;
                    }
                    else
                    {
                        individual.Note = noteText;
                    }
                    break;

                case "CONT":
                    string contText = line.GetLineContent() ?? string.Empty;
                    individual.Note += Environment.NewLine + contText;
                    break;

                case "CONC":
                    // TODO: is GenesReunited maintaining the trailing space?
                    // If so, is this correct?
                    var concText = line.GetLineContent();
                    individual.Note += concText;
                    break;

                case "BIRT":
                    var birthParseResult = EventParser.Parse(line, lineProvider);
                    if (individual.Birth == null)
                    {
                        individual.Birth = birthParseResult.Result;
                    }
                    line    = birthParseResult.Line;
                    newLine = true;
                    break;

                case "DEAT":
                    var deathParseResult = EventParser.Parse(line, lineProvider);
                    if (individual.Death == null)
                    {
                        individual.Death = deathParseResult.Result;
                    }
                    line    = deathParseResult.Line;
                    newLine = true;
                    break;

                case "BAPM":
                    var baptismParseResult = EventParser.Parse(line, lineProvider);
                    if (individual.Baptism == null)
                    {
                        individual.Baptism = baptismParseResult.Result;
                    }
                    line    = baptismParseResult.Line;
                    newLine = true;
                    break;

                case "RESI":
                    var residenceParseResult = EventParser.Parse(line, lineProvider);
                    individual.Residences.Add(residenceParseResult.Result);
                    line    = residenceParseResult.Line;
                    newLine = true;
                    break;

                case "CENS":
                    var censusParseResult = EventParser.Parse(line, lineProvider);
                    individual.Census.Add(censusParseResult.Result);
                    line    = censusParseResult.Line;
                    newLine = true;
                    break;

                case "MARR":
                    var marriageParseResult = EventParser.Parse(line, lineProvider);
                    if (marriageParseResult.Result != null)
                    {
                        individual.Marriages.Add(marriageParseResult.Result);
                    }
                    line    = marriageParseResult.Line;
                    newLine = true;
                    // TODO: cope with multiple marriage records
                    // It seems that ancestry only outputs multiple marriage records in an individual
                    //  if there's conflicting information, not for multiple marriages
                    // Multiple marriages for an individual are put into the FAMs tag
                    break;
                    //case "ENGA":
                    //    // TODO:
                    //    // individual.Engagement = EventParser.Parse(parserLinesEnga);
                    //    break;
                }

                if (line.Level == 0)
                {
                    break;
                }
            }

            if (individual.LastName == null)
            {
                individual.LastName = string.Empty;
            }

            if (individual.FirstName == null)
            {
                individual.FirstName = string.Empty;
            }

            if (individual.FirstNames == null)
            {
                individual.FirstNames = string.Empty;
            }

            return(ParseResult.Create(individual, line));
        }
예제 #10
0
        public static ParseResult <Family> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            var family = new Family();

            family.ID = ParserHelper.ParseID(first.GetTagOrRef());

            bool inMarriage = false;

            var initialLevel = first.Level;

            GedcomLine line = default;
            string     currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()) != null)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == first.Level)
                {
                    break;
                }

                switch (line.GetTagOrRef())
                {
                case "MARR":
                {
                    inMarriage = true;
                    break;
                }

                case "DATE":
                    if (inMarriage)     // TODO: should have MARR parser
                    {
                        var date = line.GetLineContent();
                        if (family.Marriage == null)
                        {
                            family.Marriage = new GedcomEvent();
                        }
                        family.Marriage.Date = date;
                    }
                    break;

                case "PLAC":
                    if (inMarriage)     // Assume level + 1 is MARR
                    {
                        var place = line.GetLineContent();
                        if (family.Marriage == null)
                        {
                            family.Marriage = new GedcomEvent();
                        }
                        family.Marriage.Location = place;
                    }
                    break;

                case "HUSB":
                    // Ignore any husband and wife information in the middle of a marriage tag.
                    // Present for torture test files - and info redundant?
                    // can have e.g. "2 HUSB", with no additional info
                    var contentHusb = line.GetLineContent();
                    if (!string.IsNullOrEmpty(contentHusb))
                    {
                        family.HusbandID = ParserHelper.ParseID(contentHusb);
                    }
                    break;

                case "WIFE":
                    // Ignore any husband and wife information in the middle of a marriage tag.
                    // Present for torture test files - and info redundant?
                    // can have e.g. "2 HUSB", with no additional info
                    var contentWife = line.GetLineContent();
                    if (!string.IsNullOrEmpty(contentWife))
                    {
                        family.WifeID = ParserHelper.ParseID(contentWife);
                    }
                    break;

                case "CHIL":
                    family.ChildIDs.Add(ParserHelper.ParseID(line.GetLineContent()));
                    break;

                default:
                    inMarriage = false;
                    break;
                }
            }

            return(ParseResult.Create(family, line));
        }
예제 #11
0
        public static ParseResult <Individual> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            var individual = new Individual();

            individual.ID = ParserHelper.ParseID(first.GetFirstItem()).ToString();

            GedcomLine          line = default;
            ReadOnlySpan <char> currentRawLine;
            var newLine = false;

            while (true)
            {
                // TODO: this loop is really messy, as some of the parsing
                //  works on lines as retrieved from the parser, where some
                //  other iterations call onto a sub-parser which return a final
                //  line. Should split this logic
                if (!newLine)
                {
                    currentRawLine = lineProvider.ReadLine();
                    if (currentRawLine == null)
                    {
                        break;
                    }
                    line = ParserHelper.ParseLine(currentRawLine);
                }
                newLine = false;

                if (line.Level == 0)
                {
                    break;
                }

                var tag = line.GetFirstItem();
                if (ParserHelper.Equals(tag, "NAME") &&
                    individual.LastName == null && individual.FirstNames == null)
                {
                    ParseNames(line, individual);
                }
                else if (ParserHelper.Equals(tag, "SEX"))
                {
                    individual.Gender = ParserHelper.Equals(line.GetLineContent(3), "M") ? Gender.Male : Gender.Female;
                }
                else if (ParserHelper.Equals(tag, "FAMS"))
                {
                    individual.FamilyIDSpouse = ParserHelper.ParseID(line.GetLineContent(4)).ToString();
                }
                else if (ParserHelper.Equals(tag, "FAMC"))
                {
                    individual.FamilyIDChild = ParserHelper.ParseID(line.GetLineContent(4)).ToString();
                }

                else if (ParserHelper.Equals(tag, "OBJE"))
                {
                    var content = line.GetLineContent(4);
                    if (content.Length > 0)
                    {
                        individual.ImageID = ParserHelper.ParseID(content).ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "NOTE"))
                {
                    var noteContent = line.GetLineContent(4);
                    if (noteContent.Length > 0)
                    {
                        if (noteContent[0] == '@')
                        {
                            individual.NoteID = ParserHelper.ParseID(noteContent).ToString();
                        }
                        else
                        {
                            individual.Note = noteContent.ToString();
                        }
                    }
                }
                else if (ParserHelper.Equals(tag, "CONT"))
                {
                    var contContent = line.GetLineContent(4);
                    individual.Note += Environment.NewLine + contContent.ToString();
                }
                else if (ParserHelper.Equals(tag, "CONC"))
                {
                    // TODO: is GenesReunited maintaining the trailing space?
                    // If so, is this correct?
                    var concContent = line.GetLineContent(4);
                    if (concContent.Length > 0)
                    {
                        individual.Note += concContent.ToString();
                    }
                }
                else if (ParserHelper.Equals(tag, "BIRT"))
                {
                    var birthParseResult = EventParser.Parse(line, lineProvider);
                    if (individual.Birth == null)
                    {
                        individual.Birth = birthParseResult.Result;
                    }
                    line    = birthParseResult.Line;
                    newLine = true;
                }
                else if (ParserHelper.Equals(tag, "DEAT"))
                {
                    var deathParseResult = EventParser.Parse(line, lineProvider);
                    if (individual.Death == null)
                    {
                        individual.Death = deathParseResult.Result;
                    }
                    line    = deathParseResult.Line;
                    newLine = true;
                }
                else if (ParserHelper.Equals(tag, "BAPM"))
                {
                    var baptismParseResult = EventParser.Parse(line, lineProvider);
                    if (individual.Baptism == null)
                    {
                        individual.Baptism = baptismParseResult.Result;
                    }
                    line    = baptismParseResult.Line;
                    newLine = true;
                }
                else if (ParserHelper.Equals(tag, "RESI"))
                {
                    var residenceParseResult = EventParser.Parse(line, lineProvider);
                    individual.Residences.Add(residenceParseResult.Result);
                    line    = residenceParseResult.Line;
                    newLine = true;
                }
                else if (ParserHelper.Equals(tag, "CENS"))
                {
                    var censusParseResult = EventParser.Parse(line, lineProvider);
                    individual.Census.Add(censusParseResult.Result);
                    line    = censusParseResult.Line;
                    newLine = true;
                }
                else if (ParserHelper.Equals(tag, "MARR"))
                {
                    var marriageParseResult = EventParser.Parse(line, lineProvider);
                    if (marriageParseResult.Result != null)
                    {
                        individual.Marriages.Add(marriageParseResult.Result);
                    }
                    line    = marriageParseResult.Line;
                    newLine = true;
                    // TODO: cope with multiple marriage records
                    // It seems that ancestry only outputs multiple marriage records in an individual
                    //  if there's conflicting information, not for multiple marriages
                    // Multiple marriages for an individual are put into the FAMs tag
                }
                //case "ENGA":
                //    // TODO:
                //    // individual.Engagement = EventParser.Parse(parserLinesEnga);
                //    break;

                if (line.Level == 0)
                {
                    break;
                }
            }

            if (individual.LastName == null)
            {
                individual.LastName = string.Empty;
            }

            if (individual.FirstName == null)
            {
                individual.FirstName = string.Empty;
            }

            if (individual.FirstNames == null)
            {
                individual.FirstNames = string.Empty;
            }

            return(ParseResult.Create(individual, line));
        }
예제 #12
0
        public static ParseResult <GedcomHeader> Parse(GedcomLine first, ILineProvider lineProvider)
        {
            CurrentLevel currentLevel = CurrentLevel.None;

            var header = new GedcomHeader();

            GedcomLine          line = default;
            ReadOnlySpan <char> currentRawLine;

            while ((currentRawLine = lineProvider.ReadLine()).Length > 0)
            {
                line = ParserHelper.ParseLine(currentRawLine);

                if (line.Level == 0)
                {
                    break;
                }

                if (line.Level == 1)
                {
                    var tag = line.GetFirstItem();
                    if (ParserHelper.Equals(tag, "SOUR"))
                    {
                        currentLevel = CurrentLevel.Sour;
                    }
                    else if (ParserHelper.Equals(tag, "GEDC"))
                    {
                        currentLevel = CurrentLevel.Gedc;
                    }
                    else if (ParserHelper.Equals(tag, "CHAR"))
                    {
                        header.GedcomCharacterSet = line.GetLineContent(4).ToString();
                    }
                }
                else if (line.Level == 2)
                {
                    if (currentLevel == CurrentLevel.Sour)
                    {
                        var tag = line.GetFirstItem();
                        if (ParserHelper.Equals(tag, "NAME"))
                        {
                            header.SourceName = line.GetLineContent(4).ToString();
                        }
                        else if (ParserHelper.Equals(tag, "VERS"))
                        {
                            header.SourceVers = line.GetLineContent(4).ToString();
                        }
                        else if (ParserHelper.Equals(tag, "CORP"))
                        {
                            header.SourceCorp = line.GetLineContent(4).ToString();
                        }
                    }
                    else if (currentLevel == CurrentLevel.Gedc)
                    {
                        if (ParserHelper.Equals(line.GetFirstItem(), "VERS"))
                        {
                            header.GedcomVers = line.GetLineContent(4).ToString();
                        }
                    }
                }
            }

            return(ParseResult.Create(header, line));
        }
예제 #13
0
        public void Parse(ILineProvider lineProvider)
        {
            GedcomLine lastLine = default;

            var firstRawLine = lineProvider.ReadLine();

            if (firstRawLine == null)
            {
                throw new InvalidOperationException("File empty");
            }

            var firstLine = ParserHelper.ParseLine(firstRawLine);

            if (firstLine.Level != 0 && !ParserHelper.Equals(firstLine.GetFirstItem(), "HEAD"))
            {
                throw new InvalidOperationException("GEDCOM Header Not Found");
            }

            var gedcomHeaderParse = HeaderParser.Parse(firstLine, lineProvider);

            _headerCallback?.Invoke(gedcomHeaderParse.Result);

            var newLine = gedcomHeaderParse.Line;

            lastLine = newLine;

            while (newLine.LineContent.Length > 0)
            {
                var content = newLine.GetLineContent();
                if (content.Length == 0)
                {
                    var unrecognisedRawLine = lineProvider.ReadLine();
                    if (unrecognisedRawLine != null)
                    {
                        newLine  = ParserHelper.ParseLine(unrecognisedRawLine);
                        lastLine = newLine;
                    }
                    else
                    {
                        newLine = default;
                    }
                    continue;
                }

                var unknown = false;

                if (ParserHelper.Equals(content, "INDI"))
                {
                    var individualParseResult = IndividualParser.Parse(newLine, lineProvider);
                    _individualCallback?.Invoke(individualParseResult.Result);
                    newLine  = individualParseResult.Line;
                    lastLine = newLine;
                }
                else if (ParserHelper.Equals(content, "FAM"))
                {
                    var familyParseResult = FamilyParser.Parse(newLine, lineProvider);
                    _familyCallback?.Invoke(familyParseResult.Result);
                    newLine  = familyParseResult.Line;
                    lastLine = newLine;
                }
                else if (ParserHelper.Equals(content, "NOTE"))
                {
                    var noteParseResult = NoteParser.Parse(newLine, lineProvider);
                    _noteCallback?.Invoke(noteParseResult.Result);
                    newLine  = noteParseResult.Line;
                    lastLine = newLine;
                }
                else if (ParserHelper.Equals(content, "OBJE"))
                {
                    var objParserResult = ObjectParser.Parse(newLine, lineProvider);
                    _imageCallback?.Invoke(objParserResult.Result);
                    newLine  = objParserResult.Line;
                    lastLine = newLine;
                }
                else
                {
                    var unrecognisedRawLine = lineProvider.ReadLine();
                    if (unrecognisedRawLine != null)
                    {
                        newLine  = ParserHelper.ParseLine(unrecognisedRawLine);
                        lastLine = newLine;
                    }
                    else
                    {
                        newLine = default;
                    }
                    unknown = true;
                }

                if (unknown)
                {
                    continue;
                }
            }

            if (lastLine.LineContent.Length == 0)
            {
                throw new InvalidOperationException("file contains no content");
            }

            if (lastLine.Level != 0 || !ParserHelper.Equals(lastLine.GetFirstItem(), "TRLR"))
            {
                throw new InvalidOperationException("GEDCOM TRLR not found");
            }
        }