Subversion Repositories DevTools

Rev

Rev 2163 | 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>
   /// ExportToReqProDatabase is a specialisation of CopyReqProDatabaseToMemory, designed to copy the 
   /// ReqPro database content into memory, and allow a user to select certain aspects to control a
   /// subsequent export operation.
        /// </summary>
        public class ExportToReqProDatabase : CopyReqProDatabaseToMemory
        {
      private ReqPro_object m_SelectedObject;
      private bool m_CreateFolder;
      private string m_FolderName;
      private string m_ReqType;
      private ExportForm.ExportExtent m_ExportExtent;

      private static char [] numbers = "0123456789".ToCharArray();
      private static char [] letters = "abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXY".ToCharArray();

                public ExportToReqProDatabase()
                {
                }


      /// <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 = true;

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

            Main.WriteOutput("EA Requirement export at " + exportDateTime, -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, but only if it appeared
               // as though it were created for the first time during the export, otherwise leave
               // it alone with its existing setting.
               if (RQ_Artifact.get_mode(RQ_Element) == ReqProDB_Artifact.MODE.UNDEFINED)
                  RQ_Artifact.set_traceability_mode(RQ_Element);

               ExportToReqPro(exportDateTime, out cancelled);

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

         return false;
      }


      /// <summary>
      /// Method called by the base class to convey the user selections made controlling the export
      /// operation.
      /// </summary>
      /// <param name="selectedObject"></param>
      /// <param name="createFolder"></param>
      /// <param name="folderName"></param>
      /// <param name="requirementType"></param>
      /// <param name="requirementStatus"></param>
      protected override void provideExportDecisionsToClient(ReqPro_object selectedObject,
         bool createFolder,
         string folderName,
         string requirementType,
         ExportForm.ExportExtent exportExtent)
      {
         m_SelectedObject = selectedObject;
         m_CreateFolder = createFolder;
         m_FolderName = folderName;

         // The requirement type display string in the export form is like "some requirement type(abbrev)"
         // We want just the abbrev so use string splitting to extract it.
         char [] delim = {'('};
         string [] tokens = requirementType.Split(delim, 2);
         delim[0] = ')';
         m_ReqType = tokens[1].Split(delim,2)[0];

         m_ExportExtent = exportExtent;
      }


      /// <summary>
      /// Function to schedule and control the export sequence. Here is where the real work
      /// begins of exporting requirements and traceability from EA to RequisitePro.
      /// </summary>
      /// <param name="exportDateTime"></param>
      /// <param name="cancelled"></param>
      private void ExportToReqPro(string exportDateTime, out bool cancelled)
      {
         cancelled = false;

         try
         {
            ArrayList ea_reqs;   // list of new requirements for export - assigned later from element accumulator
            ArrayList ea_mastered_reqs = new ArrayList();
            ArrayList ea_mastered_reqs_rp_guids = new ArrayList();

            // Read tag from ReqProDB element that enables/disables the capability to use EA as a master
            // repository for requirements originally created in EA. If the tag does not exist, EA mastering
            // will be allowed by default.
            bool EA_MasteringAllowed = EA_TaggedValues.Read(base.RQ_Element, Constants.TAG_EA_MASTERING_ALLOWED, Constants.DEFAULT_EA_MASTERING_ALLOWED);

            #region DETERMINE PARENT PACKAGE
            // Obtain the EA parent package so that we can scan it for existing requirement elements.
            EA.Package parentPackage;
            if (m_ExportExtent == ExportForm.ExportExtent.PACKAGE_REQS)
            {
               parentPackage = EA_ProjectBrowser.get_selected_package();
            }
            else 
            {
               // default to the ReqProDB container package. This is probably what most exports
               // will use.
               parentPackage = Main.EA_Repository.GetPackageByID( base.RQ_Element.PackageID );
            }
            #endregion

            #region GET_EXPORT_LIST
            Main.WriteOutput("Acquiring list of requirement elements in EA", -1);

            // setup the element types list to control the element accumulation
            ArrayList allowedElementTypes = new ArrayList();
            allowedElementTypes.Add("Requirement");
            //allowedElementTypes.Add("UseCase");

            if (m_ExportExtent != ExportForm.ExportExtent.SINGLE_REQ)
            {
               // create the element accumulator, and use it to find all the requirement
               // elements in the container package
               EA_ElementAccumulator reqLister = new EA_ElementAccumulator(allowedElementTypes);
               EA_Parsing.findAndProcessPackageElementsInBrowserOrder( parentPackage, reqLister, true );

               if (Main.mustAbort)
                  return;

               // Sanity check the EA requirements
               Main.WriteOutput("Sanity checking requirement elements in EA", -1);
               if (false == sanity_check_requirements(reqLister.Elements))
               {
                  cancelled = true;
                  return;
               }

               // Remove requirements from the accumulated list if they have already been linked
               // to ReqPro requirement elements, by virtue of them seeming to have a valid GUID
               // We dont actually validate the GUID, just check that one is present or not.
               ea_reqs = reqLister.Elements; 
               int i_ea_reqs;
               for (i_ea_reqs = 0; i_ea_reqs < ea_reqs.Count; ) // No index increment here
               {
                  EA.Element ea_req = (EA.Element)ea_reqs[i_ea_reqs];

                  string GUID = EA_TaggedValues.Read(ea_req, Constants.TAG_GUID);
                  if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
                  {
                     // found an EA requirement element that seems to already be linked to a 
                     // ReqPro requirement element

                     // if the element seems to be one that was mastered (created) in EA and exported
                     // to ReqPro on a previous export cycle, then collect it in a seperate list in case
                     // we need to do an update on ReqPro later on.
                     if (true == EA_TaggedValues.Read(ea_req, Constants.TAG_CREATED_IN_EA, Constants.DEFAULT_CREATED_IN_EA))
                     {
                        ea_mastered_reqs.Add(ea_req);
                        ea_mastered_reqs_rp_guids.Add(GUID);
                     }

                     // discard from the export list.
                     ea_reqs.RemoveAt(i_ea_reqs);
                     continue;
                  }
                  i_ea_reqs++;   // index increment here
               }

               if (Main.mustAbort)
                  return;
            }
            else // user wants to export a single selected requirement
            {
               EA.Element ea_ele = EA_ProjectBrowser.get_selected_element(allowedElementTypes);
               if (ea_ele == null)
               {
                  MessageBoxEx.Show(
                     "The export extent chosen (single requirement export)\n" +
                     "is not compatible with the item selected in the EA\n" +
                     "project browser. You must select a requirement object\n" +
                     "to use this export extent.",
                     "Error");
                  cancelled = true;
                  return;
               }

               string GUID = EA_TaggedValues.Read(ea_ele, Constants.TAG_GUID);
               if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
               {
                  MessageBoxEx.Show(
                     "The requirement object selected in the EA project browser,\n" +
                     "already appears to be associated with a ReqPro object.",
                     "Error");
                  cancelled = true;
                  return;
               }

               ea_reqs = new ArrayList();
               ea_reqs.Add(ea_ele);

               // Sanity check the EA requirement
               Main.WriteOutput("Sanity checking requirement element in EA", -1);
               if (false == sanity_check_requirements(ea_reqs))
               {
                  cancelled = true;
                  return;
               }
            }
            #endregion

            // Export newly mastered EA requirements to ReqPro
            if (false == export_new_EA_mastered_requirements(ref ea_reqs, ref ea_mastered_reqs, ref ea_mastered_reqs_rp_guids, out cancelled))
               return;

            // If the ReqProDB element informs us that EA mastering of requirements is allowed,
            // then we can process the update of any requirements in ReqPro that are linked to
            // requirements in EA, that seem to have originally been created in EA.
            if (EA_MasteringAllowed)
            {
               if (false == update_old_EA_mastered_requirements(ref ea_mastered_reqs, out cancelled))
                  return;
            }

            // Export/Update Requirement relationships
            export_new_EA_mastered_requirement_relationships(ref ea_reqs);

            if (EA_MasteringAllowed)
            {
               update_old_EA_mastered_requirement_relationships(ref ea_mastered_reqs);
            }

            Main.EA_Repository.RefreshModelView(parentPackage.PackageID);

            // Use historical data stored in the ReqProDB element to determine if any EA mastered requirements
            // have been deleted, leaving orphans in the ReqPro database that might need to be cleaned up
            report_orphaned_ReqPro_requirements(ref ea_mastered_reqs_rp_guids);

            Main.WriteOutput("Export Completed",-1);
            MessageBoxEx.Show("Export Completed");
         }
         catch (Exception ex)
         {
            Main.MessageBoxException(ex, "Exception (ExportToReqPro)");
         }
      }

      /// <summary>
      /// sanity check a list of requirements to make sure they all meet ReqPro's constraints
      /// </summary>
      /// <param name="al"></param>
      /// <returns></returns>
      private bool sanity_check_requirements(ArrayList al)
      {
         int numBadRequirements = 0;
         foreach (EA.Element ele in al)
         {
            if (false == requirement_meets_reqpro_constraints(ele))
            {
               numBadRequirements++;
            }

            if (Main.mustAbort)
               return false;
         }
         if (numBadRequirements > 0)
         {
            MessageBoxEx.Show(
               "One or more EA requirement objects for export do not meet the constraints\n" +
               "imposed by RequisitePro - see EA_ReqPro output tab for the list of bad objects.",
               "Error");
            return false;
         }
         return true;
      }


      /// <summary>
      /// sanity check a requirement in EA to make sure it meets ReqPro's constraints
      /// and issue and message to the output tab if it does not.
      /// </summary>
      /// <param name="ele"></param>
      /// <returns></returns>
      private bool requirement_meets_reqpro_constraints(EA.Element ele)
      {
         string tag = null;

         if (ele.Name == null)
         {
            Main.WriteOutput("???", ele.ElementID);
            Main.WriteOutput("   Element has no name", ele.ElementID);
            return false;
         }

         string untagged_name = untaggedReqName(ele.Name, ref tag, false);

         if (untagged_name.Length > 128)
         {
            Main.WriteOutput(untagged_name, ele.ElementID);
            Main.WriteOutput(string.Format("   Element name exceeds 128 char limit - actual length is {0}", untagged_name.Length), ele.ElementID);
            return false;
         }

         if (untagged_name.IndexOfAny("\r\n".ToCharArray(), 0) >= 0)
         {
            Main.WriteOutput(untagged_name, ele.ElementID);
            Main.WriteOutput("   Element name contains carriage return or line feed", ele.ElementID);
            return false;
         }

         if (ele.Notes != null && ele.Notes.Length > 16000)
         {
            Main.WriteOutput(untagged_name, ele.ElementID);
            Main.WriteOutput(string.Format("   Element notes exceeds 16000 char limit - actual length is {0}", ele.Notes.Length), ele.ElementID);
            return false;
         }

         return true;   // requirement is good
      }



      /// <summary>
      /// Exports newly EA mastered requirements to ReqPro
      /// </summary>
      /// <param name="ea_reqs"></param>
      /// <param name="ea_mastered_reqs"></param>
      /// <param name="ea_mastered_reqs_rp_guids"></param>
      /// <param name="cancelled"></param>
      /// <returns></returns>
      private bool export_new_EA_mastered_requirements(ref ArrayList ea_reqs, 
         ref ArrayList ea_mastered_reqs, 
         ref ArrayList ea_mastered_reqs_rp_guids, 
         out bool cancelled)
      {
         cancelled = false;

         // if there are any new requirements to export
         if (ea_reqs.Count > 0)
         {
            Main.WriteOutput(string.Format("Found {0} Requirements for export.", ea_reqs.Count), -1);

            ReqPro40.Requirements req_collection = ReqProDatabase.get_requirements();
            if (req_collection == null)
            {
               MessageBoxEx.Show("Could not acquire Requirement Collection\n" +
                  "from ReqPro database.\n", "Error");
               cancelled = true;
               return false;
            }

            ReqPro40.Package rp_pkg = null;

            // Obtain the ReqPro package selected by the user (if any)
            if (this.m_SelectedObject != null)
            {
               rp_pkg = ReqProDatabase.get_package(this.m_SelectedObject.iKey);

               if (this.m_CreateFolder)
               {
                  ReqPro40.Package rp_sub_pkg = ReqProDatabase.create_sub_package(rp_pkg, this.m_FolderName, "");
                  if (rp_sub_pkg != null)
                  {
                     rp_pkg = rp_sub_pkg;
                  }
               }
            }

            // process each requirement found in EA that needs exporting
            foreach(EA.Element ea_req in ea_reqs)
            {
               // get EA requirement without any temporary tag that the modeller may have given it
               string tag = null;
               string ea_req_untagged_name = untaggedReqName(ea_req.Name, ref tag, false);

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

               // If the EA requirement has a parent requirement, then in ReqPro we must organise it such 
               // that it has a parent-child hierarchical relationship
               ReqPro40.Requirement parent_rp_req = null;
               EA.Element parent_ea_req = null;
               if (ea_req.ParentID != 0)
               {
                  parent_ea_req = Main.EA_Repository.GetElementByID(ea_req.ParentID);
                  if (parent_ea_req != null)
                  {
                     // get GUID of ReqPro parent requirement
                     string parent_guid = EA_TaggedValues.Read(parent_ea_req, Constants.TAG_GUID);
                     if (parent_guid != null && parent_guid.Length > 0 && parent_guid.StartsWith("{"))
                     {
                        // Get the parent ReqPro requirement
                        parent_rp_req = ReqProDatabase.get_requirement_by_guid(parent_guid);
                     }

                     // Even though in EA, a parent-child relationship exists, we want to re-enforce that
                     // with a connection relationship too, so that when the parent and child exists on
                     // a diagram, they are shown linked together.
                     EA_Connectors.add(parent_ea_req, ea_req);
                  }
               }


               // Add the requirement to ReqPro requirement collection
               ReqPro40.Requirement rp_req = null;

               if (parent_rp_req != null)
                  rp_req = ReqProDatabase.add_requirement(req_collection, ea_req_untagged_name, ea_req.Notes, this.m_ReqType, parent_rp_req);
               else
                  rp_req = ReqProDatabase.add_requirement(req_collection, ea_req_untagged_name, ea_req.Notes, this.m_ReqType);

               if (rp_req != null)
               {
                  // Get the tag for the new requirement from ReqPro and use it to form a new name for 
                  // the EA requirement. Note that at this point in time the tag has a pending form. It
                  // only becomes correct once we do a rq_req.Save() later on, so this update has to be
                  // repeated later. This first update is just to confirm that we can actually do an 
                  // update in EA, before we commit the changes to the ReqPro database.
                  string rp_req_tag = rp_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag);

                  ea_req.Name = rp_req_tag + " " + ea_req_untagged_name;

                  // Test that the EA requirement can be updated.
                  if (false == ea_req.Update())
                  {
                     MessageBoxEx.Show(
                        "Failed to update EA requirement element.\n" +
                        "Check that the EA model is writeable.\n" +
                        "Check that the EA model is not locked by another user.\n" +
                        "Check that the EA requirement element is not currently being edited.\n\n" +
                        "Cancelling export - please retry once checks have been performed.", "Error");

                     cancelled = true;
                     return false;
                  }


                  // Commit changes to ReqPro database
                  rp_req.Save();

                  // Now locate the new requirement under the selected package in ReqPro, but we only need to do this if the
                  // requirement is a root level requirement that has no parent requirement.
                  if ((rp_pkg != null) && (parent_rp_req == null))
                  {
                     rp_pkg.AddElement(rp_req, ReqPro40.enumPackageLookups.ePackageLookup_Object, ReqPro40.enumElementTypes.eElemType_Requirement);
                  }
                     


                  // Now re-acquire the tag, which should no longer be in a pending form but have a real number
                  // assigned to it as a result of the commit we just did above.
                  rp_req_tag = rp_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag);

                  // Attempt to set any attributes in the new ReqPro requirement. 
                  exportAttributes(rp_req, ea_req, rp_req_tag);
                  rp_req.Save();

                  // Update the EA requirement for the second time, now with the correct and final tag
                  ea_req.Name = rp_req_tag + " " + ea_req_untagged_name;
                  ea_req.Update();
                  Main.WriteOutput("   (Re)Assigned Tag : " + ea_req.Name, ea_req.ElementID);

                  // Record the GUID and tag in EA tagged values. This will effectively bind the EA requirement
                  // to its new ReqPro requirement counterpart.
                  EA_TaggedValues.Write(ea_req, Constants.TAG_GUID, rp_req.GUID);
                  EA_TaggedValues.Write(ea_req, Constants.TAG_TAG, rp_req_tag);

                  // Save in the list of ReqPro GUIDs to support ReqPro orphaned requirement detection later on
                  ea_mastered_reqs_rp_guids.Add(rp_req.GUID);

                  // mark the requirement as having been created in EA.
                  EA_TaggedValues.Write(ea_req, Constants.TAG_CREATED_IN_EA, true);

                  // Update the ReqPro collection.
                  req_collection.Save();
               }
            }

            // Save details of the ReqPro package where the requirements were exported to. This info can be
            // used on the next export to auto-select the ReqPro destination package for the export, etc.
            if (rp_pkg != null)
            {
               EA_TaggedValues.Write(base.RQ_Element, Constants.TAG_LAST_EXPORT_GUID, rp_pkg.GUID);
               EA_TaggedValues.Write(base.RQ_Element, Constants.TAG_LAST_EXPORT_NAME, rp_pkg.Name);
            }
            else
            {
               // export occurred to the root package.
               EA_TaggedValues.Delete(base.RQ_Element, Constants.TAG_LAST_EXPORT_GUID);
               EA_TaggedValues.Delete(base.RQ_Element, Constants.TAG_LAST_EXPORT_NAME);
            }
         }
         else
         {
            Main.WriteOutput("Found no new requirements to add to ReqPro",-1);
         }

         Main.WriteSeperator();
         return true;
      }


      /// <summary>
      /// Updates ReqPro requirements that have masters in EA
      /// </summary>
      /// <param name="ea_mastered_reqs"></param>
      /// <param name="cancelled"></param>
      /// <returns></returns>
      private bool update_old_EA_mastered_requirements(ref ArrayList ea_mastered_reqs, 
         out bool cancelled)
      {
         cancelled = false;

         // if there are any EA mastered requirements...
         if (ea_mastered_reqs.Count > 0)
         {
            bool possibleOrphans = false;

            Main.WriteOutput(string.Format("Found {0} Requirements to update.", ea_mastered_reqs.Count), -1);

            ReqPro40.Requirements req_collection = ReqProDatabase.get_requirements();

            // process each EA mastered requirement
            int i_ea_mastered_reqs = 0;
            for (i_ea_mastered_reqs = 0; i_ea_mastered_reqs < ea_mastered_reqs.Count; ) // NOTE: no count increment done here
            {
               EA.Element ea_req = (EA.Element)ea_mastered_reqs[i_ea_mastered_reqs];

               // make sure there is a GUID
               string GUID = EA_TaggedValues.Read(ea_req, Constants.TAG_GUID);
               if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
               {
                  ReqPro40.Requirement rp_req;

                  // Try and find the requirement in ReqPro
                  rp_req = ReqProDatabase.get_requirement_by_guid(GUID);
                  if (rp_req != null)
                  {
                     // copy name and notes from EA to ReqPro
                     string tag = null;
                     string ea_req_untagged_name = untaggedReqName(ea_req.Name, ref tag, true);

                     // Test that the EA requirement can be updated.
                     ea_req.Name = ea_req_untagged_name;
                     ea_req.Update();
                     if (false == ea_req.Update())
                     {
                        MessageBoxEx.Show(
                           "Failed to update EA requirement element.\n" +
                           "Check that the EA model is writeable.\n" +
                           "Check that the EA model is not locked by another user.\n" +
                           "Check that the EA requirement element is not currently being edited.\n\n" +
                           "Cancelling export - please retry once checks have been performed.", "Error");

                        cancelled = true;
                        return false;
                     }

                     // copy content to the existing ReqPro requirement
                     rp_req.Name = ea_req_untagged_name;
                     rp_req.Text = ea_req.Notes;

                     string rp_req_tag = rp_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag);

                     // Attempt to set any attributes in the existing ReqPro requirement. 
                     exportAttributes(rp_req, ea_req, rp_req_tag);

                     // Commit changes to ReqPro database
                     rp_req.Save();

                     // Although we are allowing EA to be the master repository of requirements
                     // if they were originally created in EA, we still want to use ReqPro as the
                     // master for any numbering scheme (ie. tags), so update the EA requirements
                     // Tag as necessary.
                     ea_req.Name = rp_req_tag + " " + ea_req_untagged_name;
                     ea_req.Update();
                     EA_TaggedValues.Write(ea_req, Constants.TAG_TAG, rp_req_tag);
                     Main.WriteOutput("Updated : " + ea_req.Name, ea_req.ElementID);
                  }
                  else
                  {
                     possibleOrphans = true;
                     Main.WriteOutput("Warning, failed to find ReqPro requirement counterpart (possible orphan?)", ea_req.ElementID);
                  }
                  i_ea_mastered_reqs++;
               }
               else
               {
                  ea_mastered_reqs.RemoveAt(i_ea_mastered_reqs);
                  // do not increment i_ea_mastered_reqs after here
               }
            }

            Main.WriteSeperator();

            if (possibleOrphans == true)
            {
               Main.WriteOutput("", -1);
               Main.WriteOutput("One or more possible orphans have been found", -1);
               Main.WriteOutput("This could be the result of someone accidentally deleting the requirement", -1);
               Main.WriteOutput("in the ReqPro database. Or, it could be that someone modified the GUID tagged", -1);
               Main.WriteOutput("value in EA or ReqPro (very naughty).",-1);
               Main.WriteOutput("", -1);
               Main.WriteOutput("Going forward, if the requirement must be retained, delete all of its tagged values,", -1);
               Main.WriteOutput("in EA, remove the tag from its name, and attempt another export. The EA requirement will", -1);
               Main.WriteOutput("be given a new tag number, but at least it will re-appear in the ReqPro database.", -1);
               Main.WriteOutput("", -1);
               Main.WriteOutput("If the requirement is no longer needed, it can be deleted from EA once any design", -1);
               Main.WriteOutput("impacts are understood.", -1);
               Main.WriteSeperator();
            }
         }
         else
         {
            Main.WriteOutput("No ReqPro requirements need updating from their EA masters.",-1);
            Main.WriteSeperator();
         }
         return true;
      }


      /// <summary>
      /// Export relationships for the new requirements exported from EA
      /// </summary>
      /// <param name="ea_reqs"></param>
      private void export_new_EA_mastered_requirement_relationships(ref ArrayList ea_reqs)
      {
         // Algorithm design:
         //
         // for each new EA requirement (EA.Req) exported
         //   Get the ReqPro equivalent requirement (RP.Req)
         //   for each connector (EA.C) in EA.Req
         //      get referenced requirement (EA.RefReq) from EA.C
         //      find EA.RefReq's equivalent requirement (RP.RefReq) in ReqPro
         //      Establish a traces to relationship from RP.RefReq to RP.Req

         Main.WriteOutput("Creating Trace Relationships",-1);

         // for each new EA requirement (EA.Req) exported
         foreach (EA.Element ea_req in ea_reqs)
         {
            Main.WriteOutput("   " + ea_req.Name, ea_req.ElementID);

            string GUID = EA_TaggedValues.Read(ea_req, Constants.TAG_GUID, "");
            if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
            {
               // Get the ReqPro equivalent requirement (RP.Req)
               ReqPro40.Requirement rp_req = ReqProDatabase.get_requirement_by_guid(GUID);
               if (rp_req != null)
               {
                  // for each connector (EA.C) in EA.Req
                  foreach(EA.Connector ea_c in ea_req.Connectors)
                  {
                     int destId = -1;

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

                     // and make sure we filter out self-referential connectors
                     if (destId != ea_req.ElementID)
                     {
                        // get referenced requirement (EA.RefReq) from EA.C
                        EA.Element ea_ref_req = Main.EA_Repository.GetElementByID(destId);
                        if (ea_ref_req != null && ea_ref_req.Type.Equals("Requirement"))
                        {
                           // Get the GUID from the referenced element
                           GUID = EA_TaggedValues.Read(ea_ref_req, Constants.TAG_GUID, "");
                           if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
                           {
                              // find EA.RefReq's equivalent requirement (RP.RefReq) in ReqPro
                              ReqPro40.Requirement rp_ref_req = ReqProDatabase.get_requirement_by_guid(GUID);
                              if (rp_ref_req != null)
                              {
                                 // Establish a traces to relationship from RP.RefReq to RP.Req
                                 ReqPro40.Relationships rp_ref_req_c = (ReqPro40.Relationships)rp_ref_req.TracesTo;
                                 if (rp_ref_req_c != null)
                                 {
                                    // wrap the relationship add in exception handling - In EA, a parent child relationship
                                    // can exist by virtue of the elements parentID values, and such does not prevent the
                                    // existence of a connection relationship either. But in ReqPro, it does. Once a requirement
                                    // has a "hierarchical" relationship, you cannot establish the same relationship in a 2nd
                                    // way, using for example a TracesTo relationship. Using exception handling here is probably
                                    // quicker than trying to examine what relationships already exist and act accordingly.
                                    try
                                    {
                                       ReqPro40.Relationship rp_ref_req_rel = 
                                          rp_ref_req_c.Add(rp_req,
                                          ReqPro40.enumRequirementLookups.eReqLookup_Object,
                                          null, ReqPro40.enumRelatedProjectLookups.eRelProjLookup_Empty,
                                          false);
                                       if (rp_ref_req_rel != null)
                                       {
                                          // commit
                                          rp_ref_req.Save();
                                       }
                                    }
                                    catch
                                    {
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
         Main.WriteSeperator();
      }


      /// <summary>
      /// Update ReqPro relationships for requirements mastered in EA
      /// </summary>
      /// <param name="ea_mastered_reqs"></param>
      private void update_old_EA_mastered_requirement_relationships(ref ArrayList ea_mastered_reqs)
      {
         // Algorithm design:
         //
         //   for each EA requirement (EA.Req) updated
         //     Get the ReqPro equivalent requirement (RP.Req)
         //     for each connector (EA.C) in EA.Req
         //       get referenced requirement (EA.RefReq) from EA.C
         //       find EA.RefReq's equivalent requirement (RP.RefReq) in ReqPro
         //       Establish a traces to relationship from RP.RefReq to RP.Req (if it does not already exist)
         //       
         //     for each trace-to relationship (RP.C) in RP.Req
         //       if relationship does not exist in EA.Req
         //         delete RP.C
         //
         //     for each trace-from relationship (RP.C) in RP.Req
         //       if relationship does not exist in EA.Req
         //         delete RP.C
         //

         Main.WriteOutput("Updating Trace Relationships",-1);

         // for each EA requirement (EA.Req) updated
         foreach(EA.Element ea_req in ea_mastered_reqs)
         {
            Main.WriteOutput("   " + ea_req.Name, ea_req.ElementID);

            string GUID = EA_TaggedValues.Read(ea_req, Constants.TAG_GUID, "");
            if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
            {
               // Get the ReqPro equivalent requirement (RP.Req)
               ReqPro40.Requirement rp_req = ReqProDatabase.get_requirement_by_guid(GUID);
               if (rp_req != null)
               {
                  // for each connector (EA.C) in EA.Req
                  foreach(EA.Connector ea_c in ea_req.Connectors)
                  {
                     int destId = -1;

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

                     // and make sure we filter out self-referential connectors
                     if (destId != ea_req.ElementID)
                     {
                        // get referenced requirement (EA.RefReq) from EA.C
                        EA.Element ea_ref_req = Main.EA_Repository.GetElementByID(destId);
                        if (ea_ref_req != null && ea_ref_req.Type.Equals("Requirement"))
                        {
                           // Get the GUID from the referenced element
                           GUID = EA_TaggedValues.Read(ea_ref_req, Constants.TAG_GUID, "");
                           if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
                           {
                              // find EA.RefReq's equivalent requirement (RP.RefReq) in ReqPro
                              ReqPro40.Requirement rp_ref_req = ReqProDatabase.get_requirement_by_guid(GUID);
                              if (rp_ref_req != null)
                              {
                                 // Establish a traces to relationship from RP.RefReq to RP.Req (if it does not already exist)
                                 ReqPro40.Relationships rp_ref_req_c = (ReqPro40.Relationships)rp_ref_req.TracesTo;
                                 if (rp_ref_req_c != null)
                                 {
                                    // ReqPro throws an exception if you try to create a relationship that already exists.
                                    // ReqPro doesn't seem to give any easy way to ask the question, "does a relationship
                                    // exist between A and B". It is debatable whether running a hand written function to 
                                    // answer the question is going to be any quicker than just catching the exceptions 
                                    // and ignoring them. For now though, use exception handling in this very bad way.
                                    try
                                    {
                                       ReqPro40.Relationship rp_ref_req_rel = 
                                          rp_ref_req_c.Add(rp_req,
                                          ReqPro40.enumRequirementLookups.eReqLookup_Object,
                                          null, ReqPro40.enumRelatedProjectLookups.eRelProjLookup_Empty,
                                          false);
                                       if (rp_ref_req_rel != null)
                                       {

                                          // commit
                                          rp_ref_req.Save();

                                          Main.WriteOutput("      Created Trace Relationship to " + rp_ref_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag), -1);
                                       }
                                    }
                                    catch
                                    {
                                       // do nothing
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }

                  // Examine the Traces-To relationships in ReqPro
                  int limit_numberOfTraceTo = 0;
                  if (true == rp_req.get_HasTracesTo(ref limit_numberOfTraceTo))
                  {
                     ReqPro40.Relationships rp_req_c = (ReqPro40.Relationships)rp_req.TracesTo;
                     if (rp_req_c != null)
                     {
                        // for each relationship (RP.C) in RP.Req
                        rp_req_c.MoveFirst();
                        int i_numberOfTraceTo;
                        for (i_numberOfTraceTo = 0; i_numberOfTraceTo < limit_numberOfTraceTo; i_numberOfTraceTo++)
                        {
                           // Obtain the sub-requirement from the relationship, and parse it
                           ReqPro40.Relationship rp_req_rel = rp_req_c.GetCurrentRelationship();
                        
                           ReqPro40.Requirement rp_ref_req = 
                              rp_req_rel.get_DestinationRequirement(ReqPro40.enumRequirementsWeights.eReqWeight_Heavy);
                        
                           if (rp_ref_req != null)
                           {
                              // if relationship does not exist in EA.Req
                              if (!ea_element_traces_to_or_from(ea_req, rp_ref_req.GUID))
                              {
                                 // delete RP.C
                                 rp_req_rel.Delete();
                                 Main.WriteOutput("      Deleted Trace-To Relationship to " + rp_ref_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag), -1);
                              }
                           }
                        
                           rp_req_c.MoveNext();
                        }
                        // commit
                        rp_req.Save();
                     }
                  }

                  // Examine the Traces-From relationships in ReqPro
                  int limit_numberOfTraceFrom = 0;
                  if (true == rp_req.get_HasTracesFrom(ref limit_numberOfTraceFrom))
                  {
                     ReqPro40.Relationships rp_req_c = (ReqPro40.Relationships)rp_req.TracesFrom;
                     if (rp_req_c != null)
                     {
                        // for each relationship (RP.C) in RP.Req
                        rp_req_c.MoveFirst();
                        int i_numberOfTraceFrom;
                        for (i_numberOfTraceFrom = 0; i_numberOfTraceFrom < limit_numberOfTraceFrom; i_numberOfTraceFrom++)
                        {
                           // Obtain the sub-requirement from the relationship, and parse it
                           ReqPro40.Relationship rp_req_rel = rp_req_c.GetCurrentRelationship();
                        
                           ReqPro40.Requirement rp_ref_req = 
                              rp_req_rel.get_SourceRequirement(ReqPro40.enumRequirementsWeights.eReqWeight_Heavy);
                        
                           if (rp_ref_req != null)
                           {
                              // if relationship does not exist in EA.Req
                              if (!ea_element_traces_to_or_from(ea_req, rp_ref_req.GUID))
                              {
                                 // delete RP.C
                                 rp_req_rel.Delete();
                                 Main.WriteOutput("      Deleted Trace-From Relationship to " + rp_ref_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag), -1);
                              }
                           }
                        
                           rp_req_c.MoveNext();
                        }
                        // commit
                        rp_req.Save();
                     }
                  }
               }
            }
         }
         Main.WriteSeperator();
      }


      /// <summary>
      /// Use historical data stored in the ReqProDB element to 
      /// </summary>
      /// <param name="ea_mastered_reqs_rp_guids"></param>
      private void report_orphaned_ReqPro_requirements(ref ArrayList ea_mastered_reqs_rp_guids)
      {
         // we can only report on EA mastered requirements that have been deleted, if the extent of
         // the export is total, not partial
         if (m_ExportExtent == ExportForm.ExportExtent.REQPRODB_REQS)
         {
            // Get the last list of EA mastered requirements from the ReqProDB element
            if (base.RQ_Element.Notes != null && base.RQ_Element.Notes.Length > 0)
            {
               // split it into a string array
               char [] seperators = {'\r','\n'};
               string [] options = base.RQ_Element.Notes.Split(seperators);

               ArrayList rp_orphaned_reqs = new ArrayList();
               ArrayList rp_non_orphaned_reqs = new ArrayList();

               // process the string array to accumulate list of orphaned and non-orphaned requirements
               foreach (string s in options)
               {
                  string trimmed_s = s.Trim();
                  if (trimmed_s.Length > 0)
                  {
                     if (trimmed_s.StartsWith(Constants.KEY_EXPORTED_GUID))
                     {
                        string value = trimmed_s.Substring(Constants.KEY_EXPORTED_GUID.Length);
                        if (false == ea_mastered_reqs_rp_guids.Contains(value))
                        {
                           rp_orphaned_reqs.Add(value);
                        }
                        else
                        {
                           rp_non_orphaned_reqs.Add(value);
                        }
                     }
                  }
               }

               // default decision to use when constructing export options for ReqProDB element later on
               DialogResult dlgRes = DialogResult.No;

               // report on orphaned requirements
               if (rp_orphaned_reqs.Count > 0)
               {
                  ArrayList rp_orphaned_req_strings = new ArrayList();
                  
                  foreach(string s in rp_orphaned_reqs)
                  {
                     ReqPro40.Requirement rp_req = ReqProDatabase.get_requirement_by_guid(s);
                     if (rp_req != null)
                     {
                        string rp_req_tag = rp_req.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag);
                        if (rp_req_tag != null)
                        {
                           rp_orphaned_req_strings.Add("   " + rp_req_tag + " " + rp_req.Name);
                        }
                     }
                  }

                  if (rp_orphaned_req_strings.Count > 0)
                  {
                     Main.WriteOutput("The following EA Mastered requirements in ReqPro, may no longer be needed.", -1);
                     Main.WriteOutput("i.e. they are orphaned requirements in ReqPro, because they have been deleted", -1);
                     Main.WriteOutput("in EA since the last export.", -1);
                     Main.WriteOutput("Please verify and if necessary, mark them as obsolete in ReqPro.", -1);

                     foreach(string s in rp_orphaned_req_strings)
                     {
                        Main.WriteOutput(s, -1);
                     }

                     Main.WriteSeperator();

                     // allow user to specifiy whether we should forget about the orphaned requirements
                     // the next time we do an export.
                     dlgRes = MessageBoxEx.Show(
                        "Some ReqPro requirements have been orphaned (see output\n" +
                        "tab display for more information).\n" + 
                        "Ignore these problems in future exports?", "Confirm", MessageBoxButtons.YesNo);
                  }
               }

               StringBuilder sb = new StringBuilder();

               // reconstruct non-export related options using the appropriate ReqProFilterForm static method
               // That method keeps knowledge of the import filter options localised in the right place.
               ReqProFilterForm.saveFilterSettings(options, ref sb);

               // If this export had nothing to work with from the ReqProDB options....
               if (rp_orphaned_reqs.Count == 0 && rp_non_orphaned_reqs.Count == 0)
               {
                  // The options did not contain any key values. That probably means that up till now,
                  // any export that has been performed previously, did not actually export anything.
                  // So, just add whatever we exported this time (if anything)
                  if (ea_mastered_reqs_rp_guids.Count > 0)
                  {
                     sb.Append(Constants.EXPORT_HEADING);
                     foreach (string s in ea_mastered_reqs_rp_guids)
                     {
                        sb.Append(Constants.KEY_EXPORTED_GUID);
                        sb.Append(s);
                        sb.Append("\r\n");
                     }
                  }
               }
               else
               {
                  sb.Append(Constants.EXPORT_HEADING);

                  // construct export-related options
                  if (dlgRes == DialogResult.No)
                  {
                     foreach (string s in rp_orphaned_reqs)
                     {
                        sb.Append(Constants.KEY_EXPORTED_GUID);
                        sb.Append(s);
                        sb.Append("\r\n");
                     }
                  }
                  foreach (string s in rp_non_orphaned_reqs)
                  {
                     sb.Append(Constants.KEY_EXPORTED_GUID);
                     sb.Append(s);
                     sb.Append("\r\n");

                     // if this item is in ea_mastered_reqs_rp_guids, then remove it from there so that we do not
                     // duplicate it in the options string when we process ea_mastered_reqs_rp_guids after this loop
                     // completes
                     if (ea_mastered_reqs_rp_guids.Contains(s))
                     {
                        ea_mastered_reqs_rp_guids.Remove(s);
                     }
                  }

                  // If there are any items left in ea_mastered_reqs_rp_guids, add them too
                  foreach (string s in ea_mastered_reqs_rp_guids)
                  {
                     sb.Append(Constants.KEY_EXPORTED_GUID);
                     sb.Append(s);
                     sb.Append("\r\n");
                  }
               }

               base.RQ_Element.Notes = sb.ToString();
               base.RQ_Element.Update();
            }
         }
      }


      private bool ea_element_traces_to_or_from(EA.Element ea_req, string rp_GUID)
      {
         foreach(EA.Connector ea_c in ea_req.Connectors)
         {
            int destId = -1;

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

            // and make sure we filter out self-referential connectors
            if (destId != ea_req.ElementID)
            {
               EA.Element ea_ref_req = Main.EA_Repository.GetElementByID(destId);
               if (ea_ref_req != null)
               {
                  string GUID = EA_TaggedValues.Read(ea_ref_req, Constants.TAG_GUID, "");
                  if (GUID != null && GUID.Length > 0 && GUID.StartsWith("{"))
                  {
                     if (rp_GUID.Equals(GUID))
                        return true;
                  }
               }
            }
         }

         return false;
      }


      /// <summary>
      /// Look for and strip any temporary tag from the requirement name, returning a new
      /// string to be used as the requirement name
      /// </summary>
      /// <param name="taggedReqName"></param>
      /// <returns></returns>
      private string untaggedReqName(string taggedReqName, ref string tag, bool force)
      {
         if (taggedReqName != null)
         {
            // Try and split the string into two tokens at the first space character
            char [] delim = {' '};
            string [] tokens = taggedReqName.Split(delim, 2);

            // if there are two tokens resulting from the split
            if (tokens.GetLength(0) == 2)
            {
               // return tag
               tag = tokens[0];

               // if the first token appears to be a tag
               if (force == true
                  || tokens[0].StartsWith("REQ")
                  || tokens[0].StartsWith("TAG")
                  || tokens[0].StartsWith("SPR")
                  || tokens[0].StartsWith("<")
                  || (tokens[0].IndexOfAny(numbers) > 0 && tokens[0].IndexOfAny(letters) == 0))
               {
                  // return the token after the tag.
                  return tokens[1];
               }
            }
         }
         // default to the input string
         return taggedReqName;
      }

      private string tagPrefix(string tag)
      {
         if (tag != null && tag.Length > 0)
         {
            string[] prefixes = tag.Split("0123456789.".ToCharArray());
            if (prefixes.GetLength(0) > 0)
            {
               return prefixes[0];
            }
         }
         return tag;
      }

      private void exportAttributes(ReqPro40.Requirement rp_req, EA.Element ea_req, string rp_req_tag)
      {
         // Update the attributes of a ReqPro requirement. We have to be very careful here.
         // ReqPro does not like us trying to set the value of attributes that do not exist for the
         // specified requirement type, and it also does not like us trying to set an attribute that is
         // a list type, to a value that is not in the predefined set of values for the list type.
         bool hasStatus = false;
         bool hasDifficulty = false;
         bool hasPriority = false;
         bool hasSource = false;
         bool hasSourceVersion = false;
         bool hasSourceSection = false;
         bool hasSubsystem = false;
         bool hasStability = false;
         bool hasType = false;

         string reqType = tagPrefix(rp_req_tag);

         // Use the ReqProParser's knowledge of requirement types and attribute type associations to tell
         // us what attributes we can set, and then use the setListItemValue() function to do the setting.
         // That function knows how to set List items correctly and safely so as to not cause exceptions
         // to be raised in the ReqPro automation onject.
         if (reqTypeHasOneOrMoreAttrs(reqType,
            ref hasStatus, ref hasDifficulty, ref hasPriority,
            ref hasSource, ref hasSourceVersion, ref hasSourceSection,
            ref hasSubsystem, ref hasStability, ref hasType))
         {
            if (hasStatus)
            {
               setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_STATUS, ea_req.Status, rp_req_tag);
            }
            if (hasDifficulty)
            {
               setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_DIFFICULTY, ea_req.Difficulty, rp_req_tag);
            }
            if (hasPriority)
            {
               setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_PRIORITY, ea_req.Priority, rp_req_tag);
            }
            if (hasSource)
            {
               string s = EA_TaggedValues.Read(ea_req, Constants.TAG_SOURCE);
               if (s != null && s.Length > 0)
               {
                  setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_SOURCE, s, rp_req_tag);
               }
            }
            if (hasSourceVersion)
            {
               string s = EA_TaggedValues.Read(ea_req, Constants.TAG_SOURCE_VERSION);
               if (s != null && s.Length > 0)
               {
                  setTextAttrValue(rp_req, ea_req, Constants.RP_ATTR_SOURCE_VERSION, s, rp_req_tag);
               }
            }
            if (hasSourceSection)
            {
               string s = EA_TaggedValues.Read(ea_req, Constants.TAG_SOURCE_SECTION);
               if (s != null && s.Length > 0)
               {
                  setTextAttrValue(rp_req, ea_req, Constants.RP_ATTR_SOURCE_SECTION, s, rp_req_tag);
               }
            }
            if (hasSubsystem)
            {
               string s = EA_TaggedValues.Read(ea_req, Constants.TAG_SUBSYSTEM);
               if (s != null && s.Length > 0)
               {
                  // The tagged value may be a comma seperated list of subsystem names so break it up
                  // and process each one found
                  string[] sa = s.Split(",".ToCharArray());
                  foreach (string sas in sa)
                  {
                     string trimmed_sas = sas.Trim();
                     if (trimmed_sas.Length > 0)
                     {
                        setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_SUBSYSTEM_TYPE, trimmed_sas, rp_req_tag);
                     }
                  }
               }
            }
            if (hasStability)
            {
               string s = EA_TaggedValues.Read(ea_req, Constants.TAG_STABILITY);
               if (s != null && s.Length > 0)
               {
                  setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_STABILITY, s, rp_req_tag);
               }
            }
            if (hasType)
            {
               // The StereoTypeEx is a comma seperated list, and the first in the list is usually (always?)
               // the one displayed in EA's project browser, so try and export the first item in the list
               bool done = false;
               if (ea_req.StereotypeEx != null && ea_req.StereotypeEx.Length > 0)
               {
                  string[] s_arr = ea_req.StereotypeEx.Split(",".ToCharArray());
                  if (s_arr[0] != null && s_arr[0].Length > 0)
                  {
                     setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_REQ_TYPE, s_arr[0], rp_req_tag);
                     done = true;
                  }
               }
               // if we didnt get anything from StereoTypeEx, then try Stereotype
               if (!done && ea_req.Stereotype != null && ea_req.Stereotype.Length > 0)
               {
                  setListItemAttrValue(rp_req, ea_req, Constants.RP_ATTR_REQ_TYPE, ea_req.Stereotype, rp_req_tag);
               }
            }
         }
      }


      /// <summary>
      /// Set a ReqPro requirement attribute value, for an attribute that is a list type.
      /// This function only allows the setting of the value, if it is one of the values
      /// allowed for the list item. This ensures we do not get an exception raised in the
      /// ReqPro automation object.
      /// </summary>
      /// <param name="rp_req"></param>
      /// <param name="label"></param>
      /// <param name="value"></param>
      private void setListItemAttrValue(ReqPro40.Requirement rp_req, EA.Element ea_req, string label, string value, string rp_req_tag)
      {
         bool attribute_type_found = false;
         bool attribute_value_found = false;

         // look through each attribute value, for the one whose label matches the one specified
         ReqPro40.AttrValues rp_attr_values = rp_req.AttrValues;

         foreach (ReqPro40.AttrValue rp_attr_value in rp_attr_values)
         {
            if (rp_attr_value != null && rp_attr_value.Label.Equals(label))
            {
               attribute_type_found = true;

               // Look through each list item value to make sure that one exists that matches
               // the value specified
               ReqPro40.ListItemValues rp_list_item_values = rp_attr_value.ListItemValues;
               if (rp_list_item_values != null)
               {
                  foreach (ReqPro40.ListItemValue rp_list_item_value in rp_list_item_values)
                  {
                     if (rp_list_item_value.Text.Equals(value))
                     {
                        // We found that the list contains the value specified, so set the value of the attribute
                        rp_list_item_value.Selected = true;

                        attribute_value_found = true;
                        // exit inner loop
                        break;
                     }
                  }
               }
               // exit outer loop
               break;
            }
         }

         if (!attribute_type_found)
         {
            Main.WriteOutput(string.Format("WARNING, Failed to export attribute type ({0}) for ({1})",
               label, rp_req_tag), ea_req.ElementID);
         }
         if (!attribute_value_found)
         {
            Main.WriteOutput(string.Format("WARNING, Failed to export attribute type/value ({0}/{1}) for ({2})",
               label, value, rp_req_tag), ea_req.ElementID);
         }
      }



      /// <summary>
      /// Set a ReqPro requirement attribute value, for an attribute that is a text type.
      /// </summary>
      /// <param name="rp_req"></param>
      /// <param name="label"></param>
      /// <param name="value"></param>
      private void setTextAttrValue(ReqPro40.Requirement rp_req, EA.Element ea_req, string label, string value, string rp_req_tag)
      {
         bool attribute_type_found = false;

         // look through each attribute value, for the one whose label matches the one specified
         ReqPro40.AttrValues rp_attr_values = rp_req.AttrValues;

         foreach (ReqPro40.AttrValue rp_attr_value in rp_attr_values)
         {
            if (rp_attr_value != null && rp_attr_value.Label.Equals(label))
            {
               attribute_type_found = true;

               rp_attr_value.Text = value;

               // exit outer loop
               break;
            }
         }

         if (!attribute_type_found)
         {
            Main.WriteOutput(string.Format("WARNING, Failed to export attribute type ({0}) for ({1})",
               label, rp_req_tag), ea_req.ElementID);
         }
      }


        }
}