// Parser
        public static CIndividualEventStructure Parse( CGedcom gedcom, int nLevel )
        {
            CGedcomLine gedcomLine;

            // Temporary holders for class members. Saves constructing a class early.
            string sType;
            string sValue;
            string sSubtype = "";
            CEventDetail ed, eventDetail=null;

            // There must be one of these, it defines the object.
            if(
                (gedcomLine = gedcom.GetLine(nLevel, "_NMR")) == null  // Never married (brother's keeper)
                && (gedcomLine = gedcom.GetLine(nLevel, "_AKA")) == null  // Also known as (brother's keeper)
                && (gedcomLine = gedcom.GetLine(nLevel, "_AKAN")) == null // Also known as (brother's keeper)
                && (gedcomLine = gedcom.GetLine(nLevel, "BIRT")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "CHR")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "DEAT"))  == null
                && (gedcomLine = gedcom.GetLine(nLevel, "BURI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "CREM")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "ADOP")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "BAPM")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "BAP")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "BARM")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "BASM")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "BLES")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "CHRA")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "CONF")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "FCOM")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "ORDN")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "NATU")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "EMIG")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "IMMI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "CENS")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "PROB")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "WILL")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "GRAD")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "RETI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "EVEN")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "CAST")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "DSCR")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "EDUC")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "IDNO")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "NATI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "NCHI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "NMR")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "OCCU")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "PROP")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "RELI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "RESI")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "SSN")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "TITL")) == null
                && (gedcomLine = gedcom.GetLine(nLevel, "FACT")) == null
                 )
            {
                return null;
            }
            sType = gedcomLine.Tag;
            if( sType == "BAP" )
            {
                sType = "BAPM";
            }
            sValue = gedcomLine.LineItem;
            gedcom.IncrementLineIndex(1);

            bool bParsingFinished;
            do
            {
                bParsingFinished = true;
                if( (gedcomLine = gedcom.GetLine(nLevel+1, "TYPE")) != null )
                {
                    sSubtype = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                if( (gedcomLine = gedcom.GetLine(nLevel+1, "CONC")) != null )
                {
                    sValue += gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "CONT")) != null )
                {
                    sValue += "\n" + gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (ed = CEventDetail.Parse( gedcom, nLevel+1 )) != null )
                {
                    eventDetail = ed;
                    bParsingFinished = false;
                }
                else if( ( gedcomLine = gedcom.GetLine()).Level > nLevel )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, "Unknown tag :" );
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, gedcomLine.ToString() );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
            }
            while( !bParsingFinished );

            // Parsing went ok. Construct a new object and return it.
            CIndividualEventStructure ies = new CIndividualEventStructure( gedcom );
            ies.Type = sType;
            ies.m_sSubtype = sSubtype;
            ies.Value = sValue;
            ies.m_eventDetail = eventDetail;
            return ies;
        }
示例#2
0
        // Reads a GEDCOM file into a hierarchy of data structures
        public void ParseFile()
        {
            LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note,  "ParseFile()" );

            CThreadError threaderror = new CThreadError( 1, "No error" ); // 2 = process was aborted, for signalling back to calling thread. 1= cancelled by user action
            m_nLineIndex = -1; // Used to indicate to exception handling that stage2 parsing hasn't started

            FileStream fileStream = null;
            StreamReader streamReader = null;

            ClearOutParser();
            LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "ParseFile() ClearOutParser" );
            string sParseLine="";

            try
            {
                m_progressWindow.Begin( 0, 100 );
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window begun" );

                // Discern file character set by reading first 2 bytes of file
                FileStream fsCharsetDetection = new FileStream( Filename, FileMode.Open, FileAccess.Read );
                StreamReader srCharsetDetection = null;
                try
                {
                    byte[] abFirstBytes = new byte[2];
                    fsCharsetDetection.Read( abFirstBytes, 0, 2 );
                    fsCharsetDetection.Seek( 0, SeekOrigin.Begin );

                    m_ecCharset = ECharset.Unknown8bit;
                    string sCharset = "ANSI";
                    if( (abFirstBytes[0] == 0xff && abFirstBytes[1] == 0xfe) || (abFirstBytes[0] == 0x30 && abFirstBytes[1] == 0x00) )
                    {
                        m_ecCharset = ECharset.Unicode;
                    }
                    else if( (abFirstBytes[0] == 0xfe && abFirstBytes[1] == 0xff) || (abFirstBytes[0] == 0x00 && abFirstBytes[1] == 0x30) )
                    {
                        m_ecCharset = ECharset.UnicodeReversed;
                    }

                    switch( m_ecCharset )
                    {
                        case ECharset.Unicode:
                            srCharsetDetection = new StreamReader( fsCharsetDetection, System.Text.Encoding.GetEncoding("UTF-16BE") );
                            break;
                        case ECharset.UnicodeReversed:
                            srCharsetDetection = new StreamReader( fsCharsetDetection, System.Text.Encoding.GetEncoding("UTF-16LE") );
                            break;
                        default:
                            srCharsetDetection = new StreamReader( fsCharsetDetection, System.Text.Encoding.GetEncoding("utf-8") );
                            break;
                    }
                    if( srCharsetDetection != null )
                    {
                        string sCharsetDetectionLine = "";
                        do
                        {
                            sCharsetDetectionLine = srCharsetDetection.ReadLine();
                            if( sCharsetDetectionLine != "" && sCharsetDetectionLine != null )
                            {
                                sCharsetDetectionLine = sCharsetDetectionLine.ToUpper();
                                sCharsetDetectionLine = sCharsetDetectionLine.Trim();
                            }
                        }
                        while( sCharsetDetectionLine != null && ( sCharsetDetectionLine.Length < 7 || sCharsetDetectionLine.Substring(0,7) != "1 CHAR " ) );
                        if( sCharsetDetectionLine != null )
                        {
                            sCharset = sCharsetDetectionLine.Substring(7);
                            if( m_ecCharset != ECharset.Unicode && m_ecCharset != ECharset.UnicodeReversed ) // If file is in unicode format, ignore charset string cos we already know format.
                            {
                                // Using substring here to ignore trailing spaces
                                if( sCharset.Length >= 5 && sCharset.Substring(0,5) == "ASCII" )
                                {
                                    m_ecCharset = ECharset.Ascii;
                                }
                                else if( sCharset.Length >= 4 && sCharset.Substring(0,4) == "ANSI" )
                                {
                                    m_ecCharset = ECharset.Ansi;
                                }
                                else if( sCharset.Length >= 5 && sCharset.Substring(0,5) == "ANSEL" )
                                {
                                    m_ecCharset = ECharset.Ansel;
                                }
                                else if( sCharset.Length >= 4 && sCharset.Substring(0,4) == "UTF8" )
                                {
                                    m_ecCharset = ECharset.UTF8;
                                }
                                else if( sCharset.Length >= 5 && sCharset.Substring(0,5) == "UTF-8" )
                                {
                                    m_ecCharset = ECharset.UTF8;
                                }
                                else if( sCharset.Length >= 7 && sCharset.Substring(0,7) == "UNICODE" )
                                {
                                    m_ecCharset = ECharset.Unicode;
                                }
                                else if( sCharset.Length >= 5 && sCharset.Substring(0,5) == "UTF16" )
                                {
                                    m_ecCharset = ECharset.Unicode;
                                }
                                else if( sCharset.Length >= 6 && sCharset.Substring(0,6) == "UTF-16" )
                                {
                                    m_ecCharset = ECharset.Unicode;
                                }
                            }
                        }
                    }

                }
                catch( Exception e )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Caught exception while trying to discern character set :" + e.ToString() );
                    m_ecCharset = ECharset.UTF8;
                }

                if( srCharsetDetection != null )
                {
                    srCharsetDetection.Close();
                }
                if( fsCharsetDetection != null )
                {
                    fsCharsetDetection.Close();
                }

                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Opening file with charset " + m_ecCharset.ToString() );
                fileStream = new FileStream( Filename, FileMode.Open, FileAccess.Read );

                System.Text.Encoding encoding = System.Text.Encoding.GetEncoding("iso-8859-1");
                switch( m_ecCharset )
                {
                    case ECharset.Ascii:
                        encoding = System.Text.Encoding.GetEncoding("ascii");
                        break;
                    case ECharset.Ansi:
                        encoding = System.Text.Encoding.GetEncoding("iso-8859-1");
                        break;
                    case ECharset.Ansel:
                        encoding = System.Text.Encoding.GetEncoding("iso-8859-1");
                        break;
                    case ECharset.UTF8:
                        encoding = System.Text.Encoding.GetEncoding("utf-8");
                        break;
                    case ECharset.Unicode:
                        encoding = System.Text.Encoding.GetEncoding("UTF-16BE");
                        break;
                    case ECharset.UnicodeReversed:
                        encoding = System.Text.Encoding.GetEncoding("UTF-16LE");
                        break;
                    default:
                        encoding = System.Text.Encoding.GetEncoding("utf-8");
                        break;
                }

                streamReader = new StreamReader( fileStream, encoding );

                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Stream created" );

                m_nBytesRead = 0;
                m_nBytesTotal = fileStream.Length;
                uint uLineInFile = 0;

                // Read all lines in file into memory
                int nPercentComplete, nPreviousPercentComplete = 0;
                for(;;)
                {
                    if( m_progressWindow.IsAborting )
                    {

                        LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window aborting  (1)" );

                        return;
                    }

                    sParseLine = streamReader.ReadLine();
                    if( m_ecCharset == ECharset.Ansel )
                    {
                        sParseLine = ConvertAnsel( sParseLine );
                    }

                    if(  sParseLine == null )
                    {
                        LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "No more lines, end of file" );
                        // No more lines, end of file
                        break;
                    }

                    uLineInFile++;

                    m_nBytesRead += sParseLine.Length + 1; // Interim fix. Should actually count bytes according to whether gedcomLine ends with CR or CRLF.

                    nPercentComplete = (int)(m_nBytesRead * 100 / m_nBytesTotal);
                    if( nPercentComplete != nPreviousPercentComplete )
                    {
                        nPreviousPercentComplete = nPercentComplete;
                        m_progressWindow.SetText( String.Format( "Bytes read: {0}", m_nBytesRead ) );
                        if( nPercentComplete > 100 )
                        {
                            LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Percent set to 100 (1)" );

                            // Safety valve. Prevents control from throwing.
                            nPercentComplete = 100;
                        }
                        m_progressWindow.StepTo( nPercentComplete );
                    }

                    if( sParseLine.Length != 0 )
                    {
                        try
                        {
                            CGedcomLine gedcomLine = ParseLine( sParseLine, uLineInFile );
                            if( gedcomLine != null )
                            {
                                m_alLines.Add( gedcomLine );
                            }
                        }
                        catch( CParsingException )
                        {
                            LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, String.Concat("Unable to parse line ", uLineInFile.ToString(), ":", sParseLine ) );
                        }
                    }
                    // Signal waiting app that parse has finished
                }
                if( m_progressWindow.IsAborting )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window aborting  (2)" );
                    return;
                }

                // Parse lines of file
                m_nLineIndex = 0;
                sParseLine="";
                int nLines = m_alLines.Count;

                m_progressWindow.SetText( String.Format( "Parsing file line: {0} out of {1}", m_nLineIndex, nLines ) );

                nPercentComplete = (int)(m_nLineIndex * 100 / nLines);
                if( nPercentComplete > 100 )
                {
                    // Safety valve. Prevents control from throwing.
                    nPercentComplete = 100;
                }
                m_progressWindow.StepTo( nPercentComplete );
                if( m_progressWindow.IsAborting )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window aborting  (3)" );
                    return;
                }

                if( !ParseHeader() )
                {
                    // Mandatory header missing/corrupt
                    throw new CParsingException( "Header missing or corrupt" );
                }
                m_progressWindow.SetText( String.Format( "Parsing file line: {0} out of {1}", m_nLineIndex, nLines ) );
                nPercentComplete = (int)(m_nLineIndex * 100 / nLines);
                if( nPercentComplete > 100 )
                {
                    // Safety valve. Prevents control from throwing.
                    nPercentComplete = 100;
                }
                m_progressWindow.StepTo( nPercentComplete );
                if( m_progressWindow.IsAborting )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window aborting  (4)" );
                    return;
                }

                m_submissionRecord = CSubmissionRecord.Parse( this, 0 );
                m_progressWindow.SetText( String.Format( "Parsing file line: {0} out of {1}", m_nLineIndex, nLines ) );
                nPercentComplete = (int)(m_nLineIndex * 100 / nLines);
                if( nPercentComplete > 100 )
                {
                    // Safety valve. Prevents control from throwing.
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Percent set to 100 (2)" );
                    nPercentComplete = 100;
                }
                m_progressWindow.StepTo( nPercentComplete );
                if( m_progressWindow.IsAborting )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window aborting  (5)" );

                    return;
                }

                int nRecords = 0;

                CFamilyRecord fr;
                CIndividualRecord ir;
                CMultimediaRecord mr;
                CNoteRecord nr;
                CRepositoryRecord rr;
                CSourceRecord sr;
                CSubmitterRecord smr;

                CGedcomLine gedcomLine2;

                bool bParsingSuccessful = false;
                bool bParsingFinished;
                do
                {
                    bParsingFinished = false;
                    m_progressWindow.SetText( String.Format( "Parsing file line: {0} out of {1}", m_nLineIndex, nLines ) );
                    nPercentComplete = (int)(m_nLineIndex * 100 / nLines);
                    if( nPercentComplete > 100 )
                    {
                        // Safety valve. Prevents control from throwing.
                        nPercentComplete = 100;
                    }
                    m_progressWindow.StepTo( nPercentComplete );
                    if( m_progressWindow.IsAborting )
                    {
                        LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Progress window aborting  (6)" );
                        return;
                    }

                    if( (fr = CFamilyRecord.Parse( this, 0 )) != null )
                    {
                        m_alFamilyRecords.Add( fr );
                        m_htFamilyRecordsXref.Add( fr.m_xref, fr );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( (ir = CIndividualRecord.Parse( this, 0)) != null )
                    {
                        m_alIndividualRecords.Add( ir );
                        m_htIndividualRecordsXref.Add( ir.m_xref, ir );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( (mr = CMultimediaRecord.Parse( this, 0 )) != null )
                    {
                        m_alMultimediaRecords.Add( mr );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( (nr = CNoteRecord.Parse( this, 0 )) != null )
                    {
                        m_alNoteRecords.Add( nr );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( (rr = CRepositoryRecord.Parse( this, 0 )) != null )
                    {
                        m_alRepositoryRecords.Add( rr );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( (sr = CSourceRecord.Parse( this, 0 )) != null )
                    {
                        m_alSourceRecords.Add( sr );
                        m_htSourceRecordsXref.Add( sr.m_xref, sr );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( ( smr = CSubmitterRecord.Parse( this, 0 )) != null )
                    {
                        m_alSubmitterRecords.Add( smr );
                        ++nRecords;
                        bParsingFinished = false;
                    }
                    else if( ( gedcomLine2 = GetLine( 0, "TRLR" )) != null )
                    {
                        LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "TRLR found OK" );
                        bParsingSuccessful = true;
                    }
                    else
                    {
                        // Skip this unknown gedcomLine
                        LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, "WARNING: Couldn't parse line:" );
                        LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, GetLine().ToString() );
                        m_nLineIndex++;
                    }
                } // end do
                while( m_nLineIndex < nLines && !bParsingSuccessful && !bParsingFinished );
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Parsing ended normally." );

                // Tie up adopted individuals with their associated fr
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Linking adoptees." );
                foreach( CIndividualRecord adopIr in m_alAdoptedIndividuals )
                {
                    CEventDetail adopEvent = adopIr.GetEvent( "ADOP" );
                    if( adopEvent != null )
                    {
                        string adopFamXref = adopEvent.m_xrefFam;
                        bool adopHusband = adopEvent.m_bAdoptedByHusband;
                        bool adopWife = adopEvent.m_bAdoptedByWife;
                        CFamilyRecord adopFam = GetFamilyRecord( adopFamXref );
                        if( adopFam!=null && (adopHusband || adopWife) )
                        {
                            if( adopHusband )
                            {
                                CIndividualRecord irAdopHusband = GetIndividualRecord( adopFam.m_xrefHusband );
                                if( irAdopHusband != null )
                                {
                                    CIndividualEventStructure husbandAdopEvent = new CIndividualEventStructure( adopEvent );
                                    husbandAdopEvent.Type = "GEDMILL_ADOPTION_OF_CHILD"; // Special GEDmill only event
                                    husbandAdopEvent.m_eventDetail.m_xrefAdoptedChild = adopIr.m_xref;
                                    irAdopHusband.m_alIndividualEventStructures.Add( husbandAdopEvent );
                                }
                            }
                            if( adopWife )
                            {
                                CIndividualRecord irAdopWife = GetIndividualRecord( adopFam.m_xrefWife );
                                if( irAdopWife != null )
                                {
                                    CIndividualEventStructure wifeAdopEvent = new CIndividualEventStructure( adopEvent );
                                    wifeAdopEvent.Type = "GEDMILL_ADOPTION_OF_CHILD"; // Special GEDmill only event
                                    wifeAdopEvent.m_eventDetail.m_xrefAdoptedChild = adopIr.m_xref;
                                    irAdopWife.m_alIndividualEventStructures.Add( wifeAdopEvent );
                                }
                            }

                        }
                    }
                }

                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Linking indi backreferences." );
                foreach( CIndividualRecord brir in m_alIndividualRecords )
                {
                    foreach( CIndividualEventStructure ies in brir.m_alIndividualEventStructures )
                    {
                        if( ies.m_eventDetail != null )
                        {
                            foreach( CSourceCitation sc in ies.m_eventDetail.m_alSourceCitations )
                            {
                                sc.AddBackreference(new CBackReference(ERecordType.Individual, brir.m_xref, ies.Type));
                                sc.AddPicFromCitationToRecord();
                            }
                            foreach( CNoteStructure ns in ies.m_eventDetail.m_alNoteStructures )
                            {
                                if( ns != null && ns.m_alSourceCitations != null )
                                {
                                    foreach( CSourceCitation sc in ns.m_alSourceCitations )
                                    {
                                        sc.AddBackreference(new CBackReference(ERecordType.Individual, brir.m_xref, ies.Type));
                                        sc.AddPicFromCitationToRecord();
                                    }
                                }
                            }
                        }
                    }
                    foreach( CPersonalNameStructure pns in brir.m_alPersonalNameStructures )
                    {
                        if( pns.m_personalNamePieces != null )
                        {
                            foreach( CSourceCitation sc in pns.m_personalNamePieces.m_alSourceCitations )
                            {
                                sc.AddBackreference( new CBackReference( ERecordType.Individual, brir.m_xref, "NAME" ) );
                                sc.AddPicFromCitationToRecord();
                            }
                        }
                    }
                    foreach( CSourceCitation sc in brir.m_alSourceCitations )
                    {
                        sc.AddBackreference( new CBackReference( ERecordType.Individual, brir.m_xref, "" ) );
                        sc.AddPicFromCitationToRecord();
                    }
                }
                foreach( CFamilyRecord brfr in m_alFamilyRecords )
                {
                    foreach( CFamilyEventStructure fes in brfr.m_alFamilyEventStructures )
                    {
                        if( fes.m_eventDetail != null )
                        {
                            foreach( CSourceCitation sc in fes.m_eventDetail.m_alSourceCitations )
                            {
                                sc.AddBackreference(new CBackReference(ERecordType.Family, brfr.m_xref, fes.Type));
                                sc.AddPicFromCitationToRecord();
                            }
                            foreach( CNoteStructure ns in fes.m_eventDetail.m_alNoteStructures )
                            {
                                if( ns != null && ns.m_alSourceCitations != null )
                                {

                                    foreach( CSourceCitation sc in ns.m_alSourceCitations )
                                    {
                                        sc.AddBackreference(new CBackReference(ERecordType.Family, brfr.m_xref, fes.Type));
                                        sc.AddPicFromCitationToRecord();
                                    }
                                }
                            }

                        }
                    }
                    foreach( CSourceCitation sc in brfr.m_alSourceCitations )
                    {
                        sc.AddBackreference( new CBackReference( ERecordType.Family, brfr.m_xref, "" ) );
                        sc.AddPicFromCitationToRecord();
                    }

                }
                foreach( CNoteRecord brnr in m_alNoteRecords )
                {
                    foreach( CSourceCitation sc in brnr.m_alSourceCitations )
                    {
                        sc.AddBackreference( new CBackReference( ERecordType.Note, brnr.m_xref, "" ) );
                        sc.AddPicFromCitationToRecord();
                    }
                }

                // Join together fragmented multimedia files
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Joining multimedia fragments." );
                // Go through all the MFRs in every link in every record.
                foreach( CSourceRecord isr in m_alSourceRecords )
                {
                    JoinMultimedia( isr.m_alMultimediaLinks );
                }
                foreach( CIndividualRecord iir in m_alIndividualRecords )
                {
                    JoinMultimedia( iir.m_alMultimediaLinks );
                }

                // Create a list of MFRs unique to the individual
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Ordering individual's mfrs." );
                foreach( CIndividualRecord irMultimedia in m_alIndividualRecords )
                {
                    ConvertMultimediaLinks( irMultimedia.m_alMultimediaLinks, ref irMultimedia.m_alUniqueFileRefs );
                }
                // Create a list of MFRs unique to the source
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Ordering source's mfrs." );
                foreach( CSourceRecord srMultimedia in m_alSourceRecords )
                {
                    ConvertMultimediaLinks( srMultimedia.m_alMultimediaLinks, ref srMultimedia.m_alUniqueFileRefs );
                }

                // Moved inside try block as any exception it threw would not otherwise be caught:
                AddChildrenToFamilies();

                // Ended normally
                threaderror.m_nError = 0;
            }
            catch( System.Threading.ThreadAbortException e )
            {
                // Abnormal abort
                threaderror.m_nError = 2;
                threaderror.m_sMessage = "";
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Error, String.Format("Caught thread exception : {0}", e.ToString() ) );
            }
            catch( System.Threading.ThreadInterruptedException e )
            {
                // Abnormal abort
                threaderror.m_nError = 2;
                threaderror.m_sMessage = "";
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Error, String.Format( "Caught thread exception : {0}", e.ToString() ) );
            }
            catch( CParsingException e )
            {
                // Abnormal abort
                threaderror.m_nError = 2;
                threaderror.m_sMessage = "";
                string sLine = sParseLine;
                if( m_nLineIndex>=0 )
                {
                    if( m_nLineIndex >= m_alLines.Count )
                    {
                        sLine = "EOF";
                    }
                    else
                    {
                        sLine = ((CGedcomLine)(m_alLines[m_nLineIndex])).ToString();
                    }
                }

                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Error, String.Format("Caught parsing exception in file {0}, line {1} ({2}) : {3}", Filename, m_nLineIndex, sLine, e.ToString() ) );

                // And here, if we can
            }
            catch( System.IO.IOException e )
            {
                // Abnormal abort, offer retry, file already open.
                threaderror.m_nError = 3;
                threaderror.m_sMessage = "";
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Error, String.Format("Caught IO exception (line index={0}) : {1}", m_nLineIndex, e.ToString() ) );
            }
            catch( Exception e )
            {
                // Abnormal abort
                threaderror.m_nError = 2;
                threaderror.m_sMessage = "";
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Error, String.Format("Caught generic exception (line index={0}) : {1}", m_nLineIndex, e.ToString() ) );
            }
            finally
            {
                LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Closing file" );

                if (streamReader != null)
                {
                    streamReader.Close();
                }

                if (fileStream != null)
                {
                    fileStream.Close();
                }

                if( m_progressWindow != null )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "Closing progress window" );

                    m_progressWindow.End( threaderror );
                }

                // Don't need the memory any more:
                m_alLines.Clear();
            }
            LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Note, "All done." );
        }