Subversion Repositories DevTools

Rev

Rev 2173 | Blame | Compare with Previous | Last modification | View Log | RSS feed

using System;
using System.Text;
using System.Globalization;
using System.Collections;
using System.Windows.Forms;
using ReqPro40;


namespace EA_ReqPro
{

        /// <summary>
   /// ImportReqProDatabase is a specialisation of CopyReqProDatabaseToMemory, designed to copy the
   /// ReqPro database content into an EA database, discarding the structure of and hierarchy of
   /// packages, selecting just the requirements found in the ReqPro database for storage into EA,
   /// where they will be used for design traceability purposes.
        /// </summary>
   public class ImportReqProDatabase : CopyReqProDatabaseToMemory
   {
      private struct req_lists_t
      {
         public ArrayList ea_reqs;
         public ArrayList ea_GUIDs;
         public ArrayList new_ea_reqs;
         public ArrayList new_ea_GUIDs;
         public ArrayList orphaned_ea_reqs;
      };

      private req_lists_t gReq_lists = new req_lists_t();

      private int totalRequirements = 0;

      private EA.Package gCurrentPackage = null;
      private EA.Package gNewReqProDBPackage = null;
      private EA.Package gChangeHistoryPackage = null;
      private EA.Package gOrphansPackage = null;

      private int gNewRequirementCount = 0;
      private int gModifiedRequirementCount = 0;
      private int gStatusUpdatedRequirementCount = 0;
      private int gOrphanedCount = 0;

      private EA.Element gChangeLog = null;

      /// <summary>
      /// Constructor logic
      /// </summary>
      /// <param name="ea_repository"></param>
      public ImportReqProDatabase(): base()
      {
         totalRequirements = 0;
      }


      /// <summary>
      /// Method to parse a ReqPro database using the information in a ReqProDB artifact,
      /// assumed to be selected in EA's project browser prior to the method call.
      /// </summary>
      /// <param name="ea_repository"></param>
      /// <returns></returns>
      public override bool prompt_and_parse(ReqProDB_Artifact.MODE mode, out bool cancelled)
      {
         cancelled = false;

         try
         {
            base.structure_only = false;

            string importDateTime = System.DateTime.Now.ToString();

            Main.WriteOutput("EA Requirement import at " + importDateTime, -1 );
            Main.WriteOutput("",-1);

            if (true == base.prompt_and_parse(mode, out cancelled))
            {
               if (Main.mustAbort)
                  return false;

               Main.EA_Repository.EnsureOutputVisible(Main.GUI_OUTPUT_TAB_NAME);

               // Configure the ReqProDB artifact as a traceability artifact
               RQ_Artifact.set_traceability_mode(RQ_Element);

               ImportFromReqPro(importDateTime);

               return true;
            }
         }
         catch (Exception ex)
         {
            Main.MessageBoxException(ex, "Exception (parse)");
         }

         return false;
      }


      /// <summary>
      /// Displays the content of the change log element to the output trace display. This method must
      /// be kept in sync with the ImportFromReqPro method, obviously.
      /// </summary>
      /// <param name="ea_repository"></param>
      /// <param name="changeLog"></param>
      public void displayChangeLog(EA.Element changeLog)
      {
         int numberOfChangesForDeletedElements = 0;

         Main.WriteOutput("Displaying Import Change Log", -1);

         if (changeLog.Notes == null || changeLog.Notes.Length == 0 || changeLog.Notes.Trim().Length == 0)
         {
            Main.WriteOutput("  No changes", -1);
         }
         else
         {
            // The change log element contains all the log in its notes section. Break it up into
            // lines, because each line is a single log entry, so that we can process them one at
            // a time.
            string delimStr = "\n";
            char [] delim = delimStr.ToCharArray();
            string[] log_strings = changeLog.Notes.Split(delim,2000);


            // modify delimiters to : character so that we can extract the parameters for each log entry
            delimStr = ":";
            delim = delimStr.ToCharArray();

            foreach(string s in log_strings)
            {
               if (Main.mustAbort)
                  break;

               // over time, users may delete the orphaned requirements from the EA database, but their
               // GUIDs will still exist in the change log. Use of such a GUID will cause
               // an exception in the GetElementByGuid() function. What should we do? We can attempt
               // to remove those GUIDs from the list. We can do this by accumulating a new list of GUIDs
               // that is formed from the old list, where only good items from the old list find their
               // way into the new list. Gradually, the list is wittled away as EA users delete orphaned
               // requirements having first dealt with the design change impacts that resulted from their
               // orphanage.
               try
               {
                  if (s.StartsWith("{"))
                  {
                     string trimmed_s = s.Trim();

                     // Get log entry;s parameters.
                     string[] parameters = trimmed_s.Split(delim, 10);

                     // The first parameter is the GUID so use it to get the requirement element
                     EA.Element ea_req = Main.EA_Repository.GetElementByGuid(parameters[0]);

                     // Now discriminate actions based on the second parameter

                     if (parameters[1].StartsWith("Created"))
                     {
                        // user may have deleted old orphaned requirements so we would not be able to get the EA
                        // element so we cannot use it for display purposes at all.
                        if (ea_req == null)
                        {
                           Main.WriteOutput("  Created : " + parameters[2], -1 );
                        }
                        else
                        {
                           Main.WriteOutput("  Created : " + ea_req.Name, ea_req.ElementID );
                        }
                     }
                        /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                     else if (parameters[1].StartsWith("Orphaned"))
                     {
                        // user may have deleted old orphaned requirements so we would not be able to get the EA
                        // element so we cannot use it for display purposes at all.
                        if (ea_req == null)
                        {
                           Main.WriteOutput("  Orphaned : " + parameters[2], -1 );
                        }
                        else
                        {
                           // We have the EA element so do a full display of the orphaned item
                           Main.WriteOutput("  Orphaned : " + ea_req.Name, ea_req.ElementID );

                           foreach (EA.Connector theConnector in ea_req.Connectors)
                           {
                              int destId = -1;

                              // we dont care about direction of relationship, so test for both
                              if (theConnector.ClientID == ea_req.ElementID)
                                 destId = theConnector.SupplierID;
                              else if (theConnector.SupplierID == ea_req.ElementID)
                                 destId = theConnector.ClientID;
                              else
                                 destId = theConnector.SupplierID;

                              // and make sure we filter out self-referential connectors
                              if (destId != ea_req.ElementID)
                              {
                                 EA.Package tgt_pkg = (EA.Package)Main.EA_Repository.GetPackageByID( destId );
                                 if (tgt_pkg != null)
                                 {
                                    Main.WriteOutput("     --> " + tgt_pkg.Name, tgt_pkg.PackageID);
                                 }
                                 else
                                 {
                                    EA.Element tgt_ele = (EA.Element)Main.EA_Repository.GetElementByID( destId );
                                    if (tgt_ele != null)
                                    {
                                       //if (!tgt_ele.Type.StartsWith("Requirement"))
                                       Main.WriteOutput("     --> " + tgt_ele.Name, tgt_ele.ElementID);
                                    }
                                 }
                              }
                           }
                        }
                     }
                     else
                     {
                        if (ea_req != null)
                        {
                           /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           if (parameters[1].StartsWith(Constants.CHG_LOG_NAME))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_NAME, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_NOTES)) // aka Description of the requirement
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", "Description", ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_DIFFICULTY))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_DIFFICULTY, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_PRIORITY))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_PRIORITY, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_VERSION))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_VERSION, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_STATUS))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_STATUS, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_SUBSYSTEM_TYPE))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_SUBSYSTEM_TYPE, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_STABILITY))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_STABILITY, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_REQ_TYPE))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_REQ_TYPE, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_SOURCE_VERSION))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_SOURCE_VERSION, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_SOURCE_SECTION))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_SOURCE_SECTION, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                              /////////////////////////////////////////////////////////////////////////////////////////////////////////////
                           else if (parameters[1].StartsWith(Constants.CHG_LOG_SOURCE))
                           {
                              Main.WriteOutput(string.Format("  Modified {0}: {1}", Constants.CHG_LOG_SOURCE, ea_req.Name), ea_req.ElementID );
                              Main.WriteOutput("     <<< " + parameters[2], ea_req.ElementID);
                              Main.WriteOutput("     >>> " + parameters[3], ea_req.ElementID);
                           }
                        }
                        else
                        {
                           numberOfChangesForDeletedElements++;
                        }
                     }
                  }
               }
               catch
               {
               }
            }
         }
         if (numberOfChangesForDeletedElements > 0)
         {
            Main.WriteOutput("NOTE: Some propert changes were not shown because the requirement elements have since been deleted", -1);
            Main.WriteOutput("and it is impossible to use the change log recorded GUIDs to determine what they were.", -1);
         }
      }


      /// <summary>
      /// Rid a string of any : char because we use that as a delimiter in the change log and so we
      /// do not want a users use of the character to confuse the change log display mechanism.
      /// </summary>
      /// <param name="s"></param>
      /// <returns></returns>
      private string NO_COLONS(string s)
      {
         string sc =  s.Replace(':', ' ');
         sc = sc.Replace('\r' , ' ');
         sc = sc.Replace('\n' , ' ');

         return sc;
      }




      /// <summary>
      /// A method that imports requirements from a ReqPro database into EA for the purpose
      /// of supporting requirement-to-design traceability.
      /// </summary>
      /// <param name="repository"></param>
      private void ImportFromReqPro(string importDateTime)
      {
         try
         {
            DialogResult dlgres;

            // begin to initialise some of our element lists
            gReq_lists.new_ea_reqs = new ArrayList();
            gReq_lists.new_ea_GUIDs = new ArrayList();
            gReq_lists.ea_GUIDs = new ArrayList();
            gReq_lists.orphaned_ea_reqs = new ArrayList();

            ArrayList ea_req_matched = new ArrayList();

            // Obtain the EA parent package so that we can create under it our ReqProDB structure and change history packages
            EA.Package parentPackage = Main.EA_Repository.GetPackageByID( base.RQ_Element.PackageID );

            // Find or create the change history package in which to store change logs
            gChangeHistoryPackage = find_or_create_change_history_package(parentPackage);

            // create the ReqProDB structure package in the parent container package, and attach it to its counterpart
            // in the ReqPro_object tree structure (ie. attach it to the root node therein).
            gNewReqProDBPackage = (EA.Package)parentPackage.Packages.AddNew( "ReqProDB " + importDateTime, "Package");
            gNewReqProDBPackage.Update();
            rq_root_package.ea_element_ID = gNewReqProDBPackage.PackageID;

            // Migrate the older form of import directory and change log, into the newer format
            migrate_old_import_packages_to_change_history_package(parentPackage);
            Main.EA_Repository.RefreshModelView(parentPackage.PackageID);

            if (Main.mustAbort)
               return;

            // Create a package to hold orphans. This will be removed later on if it ends up empty.
            gOrphansPackage = find_or_create_orphans_package(parentPackage);

            // Get a list of EA requirements that already exist under the overall ReqProDB container package
            ArrayList allowedElementTypes = new ArrayList();
            allowedElementTypes.Add("Requirement");
            allowedElementTypes.Add("UseCase");

            Main.WriteOutput("Acquiring list of elements already in EA", -1);
            EA_ElementAccumulator reqLister = new EA_ElementAccumulator(allowedElementTypes);
            EA_Parsing.findAndProcessPackageElements( parentPackage, reqLister, true );

            gReq_lists.ea_reqs = reqLister.Elements;

            if (Main.mustAbort)
               return;

            // For the list we just found, get an adjacent list of the associated ReqPro GUIDs.
            // The two lists will be indexable by the same index.
            foreach (EA.Element ea_req in gReq_lists.ea_reqs)
            {
               string GUID = EA_TaggedValues.Read(ea_req, Constants.TAG_GUID);
               gReq_lists.ea_GUIDs.Add(GUID);
               ea_req_matched.Add(false);
            }

            if (Main.mustAbort)
               return;


            // Create the change log element in the change history package
            gChangeLog = (EA.Element)gChangeHistoryPackage.Elements.AddNew("Change Log" + " - import " + importDateTime, "InformationItem");
            gChangeLog.Notes = "";
            gChangeLog.TreePos = gChangeHistoryPackage.Elements.Count - 1;
            gChangeLog.Update();

            // Get a flattened list of requirements from the hierarchical data the user has filtered
            ArrayList rq_req_collection = new ArrayList();
            get_rq_req_collection(ref rq_req_collection);

            // initialise some statistical vars
            gNewRequirementCount = 0;
            gModifiedRequirementCount = 0;
            gStatusUpdatedRequirementCount = 0;
            gOrphanedCount = 0;


            Main.WriteOutput("Merging ReqPro content to EA", -1);

            // loop through all of the ReqPro requirements we have captured from the ReqPro database
            foreach(ReqPro_object rq_obj in rq_req_collection)
            {
               if (Main.mustAbort)
                  break;

               try
               {
                  // Find which EA requirement element refers to the current ReqPro GUID
                  int i_ea_req;
                  i_ea_req = gReq_lists.ea_GUIDs.IndexOf( rq_obj.guid );

                  // If EA element was found
                  if (0 <= i_ea_req)
                  {
                     // update orphanage detection data
                     ea_req_matched[i_ea_req] = true;
                     rq_obj.matched = true;

                     EA.Element ea_req = ((EA.Element)gReq_lists.ea_reqs[i_ea_req]);

                     rq_obj.ea_element_ID = ea_req.ElementID;

                     // Only update the requirement if it was not mastered in EA, or
                     // unrestricted imports have been enabled
                     if (  (false == EA_TaggedValues.Read(ea_req, Constants.TAG_CREATED_IN_EA, Constants.DEFAULT_CREATED_IN_EA))
                        || (true == EA_TaggedValues.Read(ea_req, Constants.TAG_UNRESTRICTED_IMPORTS, Constants.DEFAULT_UNRESTRICTED_IMPORTS)) )
                     {
                        // This ReqPro requirement already has a counterpart in EA, so we must simply
                        // update the counterpart. But, to aid the designer, we need to try to tell them
                        // how the requirement has changed, which could ofcoarse change the meaning
                        // of the requirement and require design changes to be done.

                        bool meaningMayHaveChanged = false;
                        bool statusMayHaveChanged = false;

                        string ea_req_name = rq_obj.tag + " " + rq_obj.name;

                        Main.WriteOutput("Updating: " + ea_req_name, rq_obj.ea_element_ID);

                        ea_req.Name       = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Name, Constants.CHG_LOG_NAME, ea_req_name, ref meaningMayHaveChanged);

                        ea_req.Notes      = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Notes, Constants.CHG_LOG_NOTES, rq_obj.text, ref meaningMayHaveChanged);

                        ea_req.Difficulty = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Difficulty, Constants.CHG_LOG_DIFFICULTY, rq_obj.difficulty, ref statusMayHaveChanged);

                        ea_req.Priority   = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Priority, Constants.CHG_LOG_PRIORITY, rq_obj.priority, ref statusMayHaveChanged);

                        ea_req.Status     = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Status, Constants.CHG_LOG_STATUS, rq_obj.status, ref statusMayHaveChanged);

                        if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SOURCE, Constants.CHG_LOG_SOURCE, rq_obj.source, ref statusMayHaveChanged);

                        if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SOURCE_VERSION, Constants.CHG_LOG_SOURCE_VERSION, rq_obj.sourceVersion, ref statusMayHaveChanged);

                        if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SOURCE_SECTION, Constants.CHG_LOG_SOURCE_SECTION, rq_obj.sourceSection, ref statusMayHaveChanged);

                        if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SUBSYSTEM, Constants.CHG_LOG_SUBSYSTEM_TYPE, rq_obj.subsystem, ref statusMayHaveChanged);

                        if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_STABILITY, Constants.CHG_LOG_STABILITY, rq_obj.stability, ref statusMayHaveChanged);

                        // Requirement Types are actually implemented using ordinary EA element stereotypes.
                        string[] sar = ea_req.StereotypeEx.Split(",".ToCharArray());

                        // if we didnt get a requirement type from the ReqPro database, set it to an empty string so that it can
                        // be compared to the EA stereotype strings.
                        if (rq_obj.type == null)
                           rq_obj.type = "";

                        if (sar.Length > 0)
                        {
                           // The first item in the stereotype list is the one on display in the EA element properties and so is the one the user
                           // thinks is assigned to the requirement element, so check against that for changes
                           string sar0 = sar[0].Trim();

                           if (!sar0.Equals(rq_obj.type))
                           {
                              gChangeLog.Notes += ea_req.ElementGUID + ":" + Constants.CHG_LOG_REQ_TYPE + ":" + sar0 + ":" + rq_obj.type + "\r\n";
                              gChangeLog.Update();
                              statusMayHaveChanged = true;
                              ea_req.StereotypeEx = rq_obj.type;
                              ea_req.Stereotype = rq_obj.type;
                           }
                        }
                        else if (!ea_req.Stereotype.Equals(rq_obj.type))
                        {
                           gChangeLog.Notes += ea_req.ElementGUID + ":" + Constants.CHG_LOG_REQ_TYPE + ":" + ea_req.Stereotype + ":" + rq_obj.type + "\r\n";
                           gChangeLog.Update();
                           statusMayHaveChanged = true;
                           ea_req.StereotypeEx = rq_obj.type;
                           ea_req.Stereotype = rq_obj.type;
                        }

                        ea_req.Update();

                        if (meaningMayHaveChanged)
                        {
                           gModifiedRequirementCount++;
                        }

                        if (statusMayHaveChanged)
                        {
                           gStatusUpdatedRequirementCount++;
                        }

                        totalRequirements++;
                     }
                  }
                  else
                  {
                     // update orphanage detection data
                     rq_obj.matched = true;

                     totalRequirements++;

                     // This ReqPro requirement does not have a counterpart in EA, so we must create
                     // a new one.
                     gNewRequirementCount++;

                     // create the new EA requirement in the import package
                     EA.Element ea_req = (EA.Element)gChangeHistoryPackage.Elements.AddNew( rq_obj.tag + " " + rq_obj.name, "Requirement");

                     rq_obj.ea_element_ID = ea_req.ElementID;

                     CopyReqProDatabase.copyReq(ea_req, rq_obj);

                     ea_req.Notes = rq_obj.text;

                     ea_req.Update();

                     // add the new requirement to our list for later processing
                     gReq_lists.new_ea_reqs.Add(ea_req);
                     gReq_lists.new_ea_GUIDs.Add(rq_obj.guid);

                     gChangeLog.Notes += ea_req.ElementGUID + ":Created:" + ea_req.Name + "\r\n";
                     gChangeLog.Update();

                     Main.WriteOutput("Created: " + ea_req.Name, ea_req.ElementID);
                  }

               }
               catch (Exception ex)
               {
                  Main.WriteOutput("Exception (ImportFromReqPro), " + ex.Message + ", " + rq_obj.name, -1);
               }
            }

            if (Main.mustAbort)
               return;

            Main.WriteOutput("Checking for orphaned requirements", -1);

            // Now do initial check for orphaned EA requirements
            for (int i = 0; i < gReq_lists.ea_GUIDs.Count; i++)
            {
               if (Main.mustAbort)
                  break;

               string rq_GUID = (string)gReq_lists.ea_GUIDs[i];
               if (rq_GUID != "")
               {
                  if ((bool)ea_req_matched[i] == false)
                  {
                     EA.Element ea_req = (EA.Element)gReq_lists.ea_reqs[i];

                     add_to_orphan_list(ref ea_req);
                  }
               }
            }

            // Now we import the ReqPro database package structure and migrate all of our existing or
            // new requirements into it. We only create the structure we need, ie. empty package structure
            // will not be created unecessarily.
            Main.WriteOutput("Importing latest ReqPro database package structure", -1);
            populate_latest_reqprodb_structure_package(rq_root_package);
            gNewReqProDBPackage.Packages.Refresh();
            gNewReqProDBPackage.Elements.Refresh();
            gNewReqProDBPackage.Diagrams.Refresh();
            Main.EA_Repository.RefreshModelView(parentPackage.PackageID);

            if (Main.mustAbort)
               return;

            // If there is any change to report, ask user if they want to see it now, and if so, display it
            if (gChangeLog.Notes != null && gChangeLog.Notes.Length > 0)
            {
               dlgres = MessageBoxEx.Show("Display Change Log?", "Change Log", MessageBoxButtons.YesNo);
               if (dlgres == DialogResult.Yes)
                  displayChangeLog(gChangeLog);
            }

            if (Main.mustAbort)
               return;


            // Take care of orphaned packages that have been orphaned due to a reduction in the scope of import.
            //Main.WriteOutput("Re-locating orphaned requirements (if any)", -1);
            //foreach (EA.Element ea_req_orphaned in ea_reqs_orphaned)
            //{
            //   ea_req_orphaned.PackageID = gOrphansPackage.PackageID;
            //   ea_req_orphaned.Update();
            //}

            // Remove the old superseded facsimile of the ReqPro database package structure. At this point,
            // orphaned requirements left in these will be moved to the Orphaned package, before the old
            // structure package is removed.
            Main.WriteOutput("Removing old ReqPro database package structures", -1);
            remove_old_empty_structures(parentPackage, gNewReqProDBPackage);

            if (Main.mustAbort)
               return;

            // Remove other empty packages - this tidies up the ReqPro import area so that at the top level
            // all a user should see is the Change History package, ReqProDB package, and possibly an Orphans package.
            Main.WriteOutput("Pruning empty packages", -1);

            prune_empty_packages(parentPackage);

            // Refresh the project browser
            Main.EA_Repository.RefreshModelView(parentPackage.PackageID);

            // Setup the internal requirement to requirement connectivity. This is seperate from the browser
            // organisation of elements in EA, but since in ReqPro, that organisation tells part of the story
            // of requirement to requirement connectivity, it is mirrored in the internal connectivity, along
            // with explicit trace relationships setup in ReqPro.
            // The purpose of this internal connectivity is to support relationship matrix tables in documentation
            // generated by EA_DocGen, or to support diagrammatic representations of the requirements in EA,
            // where the connectivity will become apparent through links ajoining the requirement elements in the
            // diagram.
            write_traces(totalRequirements);

            if (Main.mustAbort)
               return;

            // Output any messages the underlying parser may have collected as a result of observations it has made
            // regarding the data it has read from the ReqPro database.
            writeDelayedMessages();

            // display summary stats
            Main.WriteOutput(gNewRequirementCount.ToString() + " new requirements", -1);
            Main.WriteOutput(gModifiedRequirementCount.ToString() + " requirements modified", -1);
            Main.WriteOutput(gStatusUpdatedRequirementCount.ToString() + " requirements had status changes", -1);
            Main.WriteOutput(gOrphanedCount.ToString() + " requirements were orphaned", -1);

            Main.WriteOutput("Import Completed", -1);
            MessageBoxEx.Show("Import Completed");

         }
         catch (Exception ex)
         {
            Main.MessageBoxException(ex, "Exception (ImportFromReqPro)");
         }
      }

      private string updated_property_value_if_changed(EA.Element changeLog,
         EA.Element ea_req,
         string dest_value,
         string changeLogName,
         string source_value,
         ref bool mayHaveChanged)
      {
         if (dest_value.CompareTo(source_value) != 0 )
         {
            changeLog.Notes += ea_req.ElementGUID + ":" + changeLogName+ ":" + NO_COLONS(dest_value) + ":" + NO_COLONS(source_value) + "\r\n";
            changeLog.Update();
            mayHaveChanged = true;
            return source_value;
         }
         return dest_value;
      }


      private void if_tagged_value_changed_update_it(EA.Element changeLog,
         EA.Element ea_req,
         string tagname,
         string changeLogName,
         string source_value,
         ref bool statusMayHaveChanged)
      {
         string value = EA_TaggedValues.Read(ea_req, tagname, "");
         if ((source_value != null) && (false == source_value.Equals(value)))
         {
            changeLog.Notes += ea_req.ElementGUID + ":" + changeLogName + ":" + value + ":" + source_value + "\r\n";
            changeLog.Update();
            EA_TaggedValues.Write(ea_req, tagname, source_value);
            statusMayHaveChanged = true;
         }
      }



      /// <summary>
      /// This method (along with its sibling overload) obtains a flattened view of the
      /// hierarchical requirements obtained from the ReqPro database. This accumulation
      /// takes account of the users filtering requests.
      /// </summary>
      /// <param name="ea_repository"></param>
      private void get_rq_req_collection(ref ArrayList rq_req_collection)
      {
         foreach( ReqPro_object sub_obj in rq_root_package.ReqPro_objects )
         {
            get_rq_req_collection(ref rq_req_collection, sub_obj);
         }
      }


      private void get_rq_req_collection(ref ArrayList rq_req_collection,
                                         ReqPro_object rq_obj )
      {
         if (rq_obj.isPackage)
         {
            // if the package is not filtered, or if we are allowed to dig beneath filtered
            // packages...
            if (rq_obj.filtered == false || base.allowPackageStructureFragments)
            {
               // Using recursion, scan this objects sub-objects
               foreach( ReqPro_object sub_obj in rq_obj.ReqPro_objects )
               {
                  // if the sub-object is a package, always recurse to process it, else
                  // if the sub-object is a requirement, only recurse if the containing package
                  // is not filtered, so that requirements of filtered packages are themselves
                  // filtered out by virtue of their parent packages filtering state.
                  if (  sub_obj.isPackage
                     || (sub_obj.isRequirement && rq_obj.filtered == false))
                  {
                     get_rq_req_collection(ref rq_req_collection, sub_obj);
                  }
               }
            }
         }
         else if (rq_obj.isRequirement)
         {
            if ( ! (reqTypeIsFiltered(rq_obj) || reqStatusTypeIsFiltered(rq_obj)) )
            {
               // Add this requirement to the flattened list we are accumulating
               rq_req_collection.Add(rq_obj);

               // In ReqPro, a requirement can be related to another requirement in several ways:
               // 1. By virtue of its position relative to another in the browser display. That is,
               //    if you place a requirement beneath a parent requirement, you are establishing a
               //    parent-child relationship.
               // 2. By virtue of a specific TraceTo relationship being made via the Traceability menu
               //    option.
               // Interestingly, ReqPro prevents you creating a relationship of one of the above types,
               // if a relationship of the other type already exists. This implies that the relationship
               // between a parent and child requirement must be singular and is important regardless
               // of the manner in which it was created or represented.
               // The CopyReqProDatabaseToMemory base class will already have taken care of recording
               // relationships detected from ReqPro made using method 2 above. What we need to do here is
               // take care of those apparent from the browser based hierarchical organisation (ie. made
               // in ReqPro using method 1).
               // All we need to do is grab the parent object (if any) and add the GUID of the child to
               // its list of trace-to's. Later on, the write_traces() class method will turn these into
               // EA based connection objects that belong to the parent requirement elements.
               if (rq_obj.parent != null)
               {
                  rq_obj.parent.ReqPro_traces.Add(rq_obj.guid);
               }

               // Using recursion, scan this objects sub-objects, which in practise will all
               // be requirement objects. At this time requirement objects cannot have packages
               // as children.
               foreach( ReqPro_object sub_obj in rq_obj.ReqPro_objects )
               {
                  get_rq_req_collection(ref rq_req_collection, sub_obj);
               }
            }
         }
      }






      /// <summary>
      /// Populates a ReqProDB structure package to be used as a home for the requirements within the
      /// overall import container, and re-locate requirements into it from wherever they may be elsewhere
      /// within and under the import container.
      /// </summary>
      /// <param name="rq_obj"></param>
      private void populate_latest_reqprodb_structure_package(ReqPro_object rq_obj)
      {
         foreach( ReqPro_object sub_obj in rq_obj.ReqPro_objects )
         {
            if (Main.mustAbort)
               return;

            if (sub_obj.isPackage)
            {
               populate_latest_reqprodb_structure_package(sub_obj);   // recurse
            }
            else if (sub_obj.isRequirement)
            {
               EA.Element ea_req = null;

               // Examine all of our requirement lists to try and re-locate the requirements from
               // the old structure(s) to the new one.

               // re-locate existing requirements updated during this import
               int i_ea_req;
               i_ea_req = gReq_lists.ea_GUIDs.IndexOf( sub_obj.guid );
               if (0 <= i_ea_req)
               {
                  ea_req = ((EA.Element)gReq_lists.ea_reqs[i_ea_req]);

                  EA.Package pkg = get_or_create_latest_structure(sub_obj);

                  if (sub_obj.matched == false)
                  {
                     add_to_orphan_list(ref ea_req);

                     ea_req.PackageID = gOrphansPackage.PackageID;   // re-locate
                     ea_req.Update();
                  }
                  else if (sub_obj.filtered == false)
                  {
                     ea_req.PackageID = pkg.PackageID;   // re-locate
                     ea_req.TreePos = sub_obj.treePos;
                     ea_req.Update();
                  }
                  else
                  {
                     // QUESTION: So what if it is filtered? What happens to it then?
                     // Will it stay in the same place or be relocated as an orphan?
                     // ANSWER: relocate_elements_to_orphans_package() does this, called later
                     // in the import sequence.
                  }
               }
               else
               {
                  // re-locate new requirements that have come in for the first time on this import.
                  i_ea_req = gReq_lists.new_ea_GUIDs.IndexOf( sub_obj.guid );
                  if (0 <= i_ea_req)
                  {
                     ea_req = ((EA.Element)gReq_lists.new_ea_reqs[i_ea_req]);

                     EA.Package pkg = get_or_create_latest_structure(sub_obj);

                     ea_req.PackageID = pkg.PackageID;   // re-locate
                     ea_req.TreePos = sub_obj.treePos;
                     ea_req.Update();
                  }
               }

               // Re-locate requirement under its parent requirement, if such a relationship exists.
               if (ea_req != null && sub_obj.parent.isRequirement)
               {
                  // Only do this if the parent requirement has not been orphaned or filtered out of the import
                  if (sub_obj.parent.ea_element_ID != -1)
                  {
                     EA.Element parent_ea_req = Main.EA_Repository.GetElementByID(sub_obj.parent.ea_element_ID);
                     if (parent_ea_req != null)
                     {
                        // make sure we only assign ele-to-ele relationship if both elements are in the same
                        // package
                        if (ea_req.PackageID == parent_ea_req.PackageID)
                        {
                           ea_req.ParentID = parent_ea_req.ElementID;   // re-locate
                           ea_req.TreePos = sub_obj.treePos;
                           ea_req.Update();
                        }
                     }
                  }
                  else
                  {
                     // TODO: what if the parent requirement has been orphaned, but the child as represented by
                     // the current value of sub_obj has not been? Will that have already been taken care of by
                     // the code earlier in this function that re-locates existing requirements to the correct
                     // parent package?
                  }
               }

               populate_latest_reqprodb_structure_package(sub_obj);   // recurse
            }
         }
      }


      /// <summary>
      /// This is a helper for the populate_latest_reqprodb_structure_package() function. It allows on-the-fly
      /// EA package path construction, triggered whenever we find we have a requirement to
      /// re-locate into the latest facsimile of the ReqPro database structure.
      /// </summary>
      /// <param name="rq_obj"></param>
      /// <returns></returns>
      private EA.Package get_or_create_latest_structure(ReqPro_object rq_obj)
      {
         // go up the tree to find the immediate enclosing package of the requirement represented
         // by the input parameter. We do this because it is certain that this function will be called
         // initially passing a ReqPro_object that has "isRequirement" set to true. The requirement object
         // may also be nested within another requirement object, as a result of its parent-child hierarchy
         // in ReqPro.
         while(rq_obj.isRequirement && rq_obj.parent != null)
         {
            rq_obj = rq_obj.parent;
         }

         // Now that we have reached a package, we can figure out if we need to create its facsimile in EA
         // or detect if that has already been done on a previous invocation.
         if (rq_obj.isPackage)
         {
            // if this package has already been related to an EA package, then simply return it. The EA package
            // must have been created ona  previous invocation of this function.
            if (rq_obj.ea_element_ID != -1)
            {
               return Main.EA_Repository.GetPackageByID(rq_obj.ea_element_ID);
            }
            else // need to create this package, and if necessary, its parental line right up to the root package
            {
               // use recursion to work our way up the package ancestral line until we hit a point where
               // we find an EA package, then unwind, creating the sub-packages down to where our requirement
               // is to be held.
               EA.Package pkg = get_or_create_latest_structure(rq_obj.parent);   // recurse
               if (pkg != null)
               {
                  if (rq_obj.filtered)
                  {
                     return pkg;
                  }
                  else
                  {
                     EA.Package sub_pkg = EA_Package.create(pkg, rq_obj.name, rq_obj.treePos);

                     // attach the EA package to the ReqPro_object so that we do not attempt to create this
                     // package again on another call into this function.
                     rq_obj.ea_element_ID = sub_pkg.PackageID;

                     return sub_pkg;
                  }
               }
            }
         }

         // default to returning the root level package in EA where the requirement structure begins.
         // This will always be valid and available, but hopefully, the execution path wont drop down
         // to here.
         return Main.EA_Repository.GetPackageByID(rq_root_package.ea_element_ID);
      }

      /// <summary>
      /// Remove the old ReqProDB structure packages if they are now empty of requirements. IF some
      /// requirements remain in them, they are moved to the orphaned package so that the old ReqProDB
      /// packages can be removed.
      /// </summary>
      /// <param name="parentPackage"></param>
      /// <param name="new_structure_pkg"></param>
      private void remove_old_empty_structures(EA.Package parentPackage, EA.Package new_structure_pkg)
      {
         // NOTE: The parentPackage is the container of the ReqProDB element
         short i;
         for(i=0; i<parentPackage.Packages.Count; i++)
         {
            gCurrentPackage = (EA.Package)parentPackage.Packages.GetAt(i);

            // dont do anything to the new structure package that has been created on the current import cycle.
            if (new_structure_pkg != null && gCurrentPackage.PackageID == new_structure_pkg.PackageID)
            {
               continue;
            }

            // skip any packages that are not ReqProDB structure packages
            if (!gCurrentPackage.Name.StartsWith("ReqProDB"))
            {
               continue;
            }

            // re-locate all elements to the orphans package
            relocate_elements_to_orphans_package(gCurrentPackage);

            // Now delete the old ReqProDB structure package
            parentPackage.Packages.DeleteAt(i, false);
         }

         parentPackage.Packages.Refresh();
         Main.EA_Repository.RefreshModelView(parentPackage.PackageID);
      }


      /// <summary>
      /// Re-locate all requirements that remain under a ReqProDB structure package, to its
      /// top level, so that later on, the package hierarchy within it can be removed.
      /// </summary>
      /// <param name="parentPackage"></param>
      private void relocate_elements_to_orphans_package(EA.Package parentPackage)
      {
         if (parentPackage.PackageID != gOrphansPackage.PackageID)
         {
            // relocate elements and diagrams to the top level of the orphans package
            foreach(EA.Element ele in parentPackage.Elements)
            {
               ele.PackageID = gOrphansPackage.PackageID;
               ele.Update();
            }
            foreach(EA.Diagram diag in parentPackage.Diagrams)
            {
               diag.PackageID = gOrphansPackage.PackageID;
               diag.Update();
            }

            // now parse lower level packages in the tree
            foreach(EA.Package sub_pkg in parentPackage.Packages)
            {
               relocate_elements_to_orphans_package(sub_pkg);  // recurse
            }
         }
      }


      /// <summary>
      /// This function migrates the older form of import package and its change log file into the newer
      /// Change History package, and deletes the old import package. In doing this, it will move any
      /// requirements still remaining in the import package to the top level of the new ReqProDB structure
      /// package.
      /// </summary>
      /// <param name="parentPackage"></param>
      private void migrate_old_import_packages_to_change_history_package(EA.Package parentPackage)
      {
         if (gNewReqProDBPackage != null && gChangeHistoryPackage != null)
         {
            bool found = false;
            short i;
            for(i=0; i<parentPackage.Packages.Count; i++)
            {
               EA.Package sub_pkg = (EA.Package)parentPackage.Packages.GetAt(i);

               if (sub_pkg.Name.StartsWith("import"))
               {
                  if (!found)
                  {
                     found = true;
                     Main.WriteOutput("Migrating old import package contents to Change History and ReqProDB packages", -1);
                  }

                  // re-locate all elements from the old import package to the new ReqProDB structure package.
                  migrate_old_import_packages_to_change_history_package_recurser(sub_pkg);

                  // delete the old import package
                  parentPackage.Packages.DeleteAt(i, false);
               }
            }
            parentPackage.Packages.Refresh();
            Main.EA_Repository.RefreshModelView(parentPackage.PackageID);
         }
      }

      /// <summary>
      /// Moves all elements out of an old-style import package, and into the new ReqProDB structure package.
      /// Change Log elements are handled in a special way such that they are renamed from plain "Change Log"
      /// to the new style "Change Log - import <date> <time>" format, and are moved into the new "Change History"
      /// package.
      /// </summary>
      /// <param name="parentPackage"></param>
      private void migrate_old_import_packages_to_change_history_package_recurser(EA.Package parentPackage)
      {
         foreach(EA.Element ele in parentPackage.Elements)
         {
            if (ele.Name.StartsWith("Change Log"))
            {
               ele.Name = ele.Name + " - " + parentPackage.Name;
               ele.PackageID = gChangeHistoryPackage.PackageID;
               ele.Update();
               gChangeHistoryPackage.Elements.Refresh();
            }
            else
            {
               ele.PackageID = gNewReqProDBPackage.PackageID;
               ele.Update();
            }
         }

         foreach(EA.Diagram diag in parentPackage.Diagrams)
         {
            diag.PackageID = gNewReqProDBPackage.PackageID;
            diag.Update();
         }

         foreach(EA.Package sub_pkg in parentPackage.Packages)
         {
            migrate_old_import_packages_to_change_history_package_recurser(sub_pkg);   // recurse
         }
      }


      private EA.Package find_or_create_change_history_package(EA.Package parentPackage)
      {
         foreach(EA.Package sub_pkg in parentPackage.Packages)
         {
            if (sub_pkg.Name.StartsWith("Change History"))
            {
               return sub_pkg;
            }
         }
         EA.Package pkg = EA_Package.create(parentPackage, "Change History", 1);
         return pkg;
      }

      private EA.Package find_or_create_orphans_package(EA.Package parentPackage)
      {
         foreach(EA.Package sub_pkg in parentPackage.Packages)
         {
            if (sub_pkg.Name.StartsWith("Orphaned Requirements"))
            {
               return sub_pkg;
            }
         }
         EA.Package pkg = EA_Package.create(parentPackage, "Orphaned Requirements", 1);
         return pkg;
      }


      /// <summary>
      /// This function removes any empty packages it can find in the ReqPro import area. This is
      /// a tidying up operation.
      /// </summary>
      /// <param name="parentPackage"></param>
      /// <returns></returns>
      private bool prune_empty_packages(EA.Package parentPackage)
      {
         bool retVal = false;
         bool needRefresh = false;
         short i;
         for(i=0; i<parentPackage.Packages.Count; i++)
         {
            EA.Package sub_pkg = (EA.Package)parentPackage.Packages.GetAt(i);

            if (true == prune_empty_packages(sub_pkg))   // recurse
            {
               parentPackage.Packages.DeleteAt(i, false);
               needRefresh = true;
            }
         }

         if (needRefresh)
         {
            parentPackage.Packages.Refresh();
         }

         if (parentPackage.Packages.Count == 0
            && parentPackage.Elements.Count == 0
            && parentPackage.Diagrams.Count == 0)
         {
            retVal = true;
         }

         return retVal;
      }

      private void add_to_orphan_list(ref EA.Element ea_req)
      {
         if (!gReq_lists.orphaned_ea_reqs.Contains(ea_req.ElementID))
         {
            gReq_lists.orphaned_ea_reqs.Add(ea_req.ElementID);

            gOrphanedCount++;
            gChangeLog.Notes += ea_req.ElementGUID + ":Orphaned:" + ea_req.Name + "\r\n";
            gChangeLog.Update();
         }
      }

        }


}