Subversion Repositories DevTools

Rev

Rev 2161 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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

namespace EA_ReqPro
{
        /// <summary>
        /// This class is responsible for constructing a facsimile of the reqpro database, from objects given to it 
        /// by the ReqProParser base class. It then enables the user to select particular filter settings applied
        /// to that data, to control a subsequent import into EA (not performed in this class or it's base class ).
        /// </summary>
        public class CopyReqProDatabaseToMemory : ReqProParser
        {
      // Flag to allow a client to configure this class to allow package structure fragments to be 
      // captured during an import, ie. package structures that are ticked in the filter form but whose
      // parental structur is not ticked.
      protected bool allowPackageStructureFragments = false;

      // items setup by base class calling into provideReqProDatabaseInfo()
      protected ReqProDB_Artifact RQ_Artifact;
      protected EA.Element RQ_Element;

      // Where in an EA database, the copy of the ReqPro database content will be written
      protected EA.Package ea_rootPackage;

      // Hierarchy tracking data for the parsing of a ReqPro database
      protected Stack rq_objs = new Stack();      // Top of stack indicates current parent package
      protected Stack ea_treePos = new Stack();   // Top of stack indicates current tree position reflecting object ordering within the parent package
      protected int lastLevel;                    // Allows us to determine if we have gone down/up in the hierarchy

      private ReqProObject_Dictionary guid_to_obj_dictionary;

      private ArrayList trace_log;

      // Collector for the results of the parsing
      protected ReqPro_object rq_root_package;

      private int writeTracesModulo = 100;

      /// <summary>
      /// Constructor logic
      /// </summary>
      /// <param name="ea_repository"></param>
                public CopyReqProDatabaseToMemory(): base()
                {
         // figure out where in the EA database we will place the results of parsing
         // NOTE: This just attempts to find a default value for ea_rootPackage.
         // Its final value may be overriden later in the parsing process, when the 
         // ReqProDB element is acquired (see in provideReqProDatabaseInfo() method).
         ea_rootPackage = EA_Utilities.get_selected_package();
                }



      #region Requirement Type methods
      /// <summary>
      /// Recursively set the requirement type enum in each requirement object read
      /// from the ReqPro database. The enum give fast indication of the requirement type.
      /// If we didnt do this, each time the requirement type needs to be evaluated, a string
      /// compare needs to be done. Here, we do them all up-front so that down the track a simple
      /// integer access is all that is required.
      /// </summary>
      /// <param name="rq_obj"></param>
      private void set_rq_req_types_in_copied_data( ReqPro_object rq_obj )
         {
            if (rq_obj.isRequirement)
            {
               int i = 0;
               foreach (ReqPro_ReqType req_type in rq_req_types)
               {
                  if (rq_obj.tag.StartsWith(req_type.prefix))
                  {
                     rq_obj.tag_enum = i;
                  }
                  i++;
               }
            }

            foreach( ReqPro_object sub_obj in rq_obj.ReqPro_objects )
            {
               // recurse
               set_rq_req_types_in_copied_data(sub_obj);
            }
         }


      #endregion

      #region ReqProParser (base class) overrides
      /// <summary>
      /// This method is designed to prompt the user to select the ReqPro database file
      /// before opening and parsing it. Once parsed, the user is offered a chance to setup
      /// the filter controls.
      /// </summary>
      /// <param name="ea_repository"></param>
      /// <returns></returns>
      public override bool prompt_and_parse(ReqProDB_Artifact.MODE mode, out bool cancelled)
      {
         cancelled = false;

         guid_to_obj_dictionary = new ReqProObject_Dictionary();

         try
         {
            pre_parsing();
            if (true == base.prompt_and_parse(mode, out cancelled))
            {
               // update the reqpro root package name from the actual reqpro project that has now been opened
               // and parsed. This may or may not be used depending upon the higher level function being carried 
               // out. For imports, the name is never used. For exports, it may appear as the root name in 
               // a folder view of the reqpro database package content.
               rq_root_package.name = ReqProDatabase.name();

               // make sure all imported requirements have the correct requirement type enumeration
               // (converted from the string name of the tag)
               set_rq_req_types_in_copied_data(rq_root_package);

               // Do one thing for imports, and another for exports.
               if (  mode == ReqProDB_Artifact.MODE.DOC_MODEL
                  || mode == ReqProDB_Artifact.MODE.TRACEABILITY )
               {
                  // bring up the import filter dialog to allow user to specify exactly what gets copied
                  ReqProFilterForm rq_filter = new ReqProFilterForm(mode == ReqProDB_Artifact.MODE.TRACEABILITY);
                  rq_filter.populate(rq_root_package, rq_req_types, rq_req_status_types);

                  // Setup the filter based on the saved filter settings in the ReqProDB element (if any)
                  if (RQ_Element != null)
                  {
                     rq_filter.loadFilterSettings(RQ_Element.Notes);
                  }

                  DialogResult dlgRes = rq_filter.ShowDialog();
                  if (dlgRes == DialogResult.OK)
                  {
                     allowPackageStructureFragments = rq_filter.allowPackageStructureFragments;

                     // Save filter settings to the ReqProDB element if it is available
                     if (RQ_Element != null)
                     {
                        RQ_Element.Notes = rq_filter.saveFilterSettings();
                        RQ_Element.Update();
                     }

                     return true;
                  }
                  else
                  {
                     cancelled = true;
                  }
               }
               else if (mode == ReqProDB_Artifact.MODE.EXPORT)
               {
                  ExportForm exportForm = new ExportForm();
                  exportForm.populate(rq_root_package, EA_TaggedValues.Read(RQ_Element, Constants.TAG_LAST_EXPORT_GUID, ""),
                     rq_req_types, rq_req_status_types);
                  DialogResult dlgRes = exportForm.ShowDialog();
                  if (dlgRes == DialogResult.OK)
                  {
                     // communicate user selections to the client class that will perform the export.
                     string folderName;
                     bool createFolder = exportForm.user_create_folder_selection(out folderName);

                     provideExportDecisionsToClient(exportForm.user_selected_object(), 
                        createFolder, folderName, 
                        exportForm.user_selected_requirement_type(), 
                        exportForm.user_selected_export_extent());


                     // NOTE: Do not close the reqpro database here. The client class will do that 
                     // after the export completes.
                     return true;
                  }
                  else
                  {
                     cancelled = true;
                  }
               }
            }
         }
         catch (Exception ex)
         {
            Main.MessageBoxException(ex, "Exception (parse)");
         }

         return false;
      }



      /// <summary>
      /// Function that must be overriden in a client class designed to handle the exporting of
      /// requirements back to ReqPro. The function gives such a class the details of selections made
      /// via the export form.
      /// </summary>
      /// <param name="selectedObject"></param>
      /// <param name="createFolder"></param>
      /// <param name="folderName"></param>
      /// <param name="requirementType"></param>
      /// <param name="requirementStatus"></param>
      protected virtual void provideExportDecisionsToClient(ReqPro_object selectedObject,
         bool createFolder,
         string folderName,
         string requirementType,
         ExportForm.ExportExtent exportExtent)
      {
      }


      /// <summary>
      /// This method will be called by the base class parser when it has obtained a ReqPro
      /// project object. We capture that object here so we can interrogate the ReqPro database
      /// ourselves, if we need to. We wont do that for package/requirement reading, but we may
      /// do it for meta-data such as requirement types, etc.
      /// </summary>
      /// <param name="reqpro_project"></param>
      protected override void provideReqProDatabaseInfo(
         ReqProDB_Artifact rq_artifact,
         EA.Element rq_element)
      {
         RQ_Artifact = rq_artifact;
         RQ_Element = rq_element;

         // Now we have been given a ReqProDB stereotyped element, we use its parent container package
         // as our root package. This overrides the default setup during the class constructor.
         ea_rootPackage = Main.EA_Repository.GetPackageByID(RQ_Element.PackageID);
      }

 
      /// <summary>
      /// This method will be called by the base class parser whenever a package or requirement object
      /// is found in the ReqPro database. The method collects important information from the object
      /// into a structure that begins with the ea_rootPackage object. The structure is highly dynamic
      /// with each object able to hold a list of other objects. This naturally allows for the ReqPro
      /// database hierarchy to be accurately reflected. The hierarchy tracking data maintained within
      /// the method allows us to know what object in the structure to hang off any new object derived
      /// from info given to us by the base class parser (ie. what object is the parent object at the
      /// present time during the parsing).
      /// </summary>
      /// <param name="level"></param>
      /// <param name="ea_repository"></param>
      /// <param name="rq_project"></param>
      /// <param name="rq_package"></param>
      /// <param name="rq_requirement"></param>
      protected override void processObject(int level,
                                            ReqPro40.Package rq_package,
                                            ReqPro40.Requirement rq_requirement)
      {
         // If we are still at the same level as the previous package, then pop the previous object
         // in readiness for pushing the one we are now dealing with.
         if (level == lastLevel)
         {
            rq_objs.Pop();
            ea_treePos.Pop();
         }
         // but if we are beneath the previous level, pop all objects that are above us because
         // we no longer need them in our hierarchy reference data.
         else if (level < lastLevel)
         {
            while (lastLevel >= level)
            {
               rq_objs.Pop();
               ea_treePos.Pop();
               lastLevel--;
            }
         }

         // bump the tree position at this level (controls display position in the EA project browser)
         int treePos = (int)ea_treePos.Pop();
         treePos++;
         ea_treePos.Push(treePos);

         // create the new requirement or package
         ReqPro_object new_rq_obj = null;
         if (rq_requirement != null)
         {
            new_rq_obj = new ReqPro_object();

            new_rq_obj.isRequirement = true;
            new_rq_obj.name   = rq_requirement.Name;
            new_rq_obj.text   = rq_requirement.Text;
            new_rq_obj.guid   = rq_requirement.GUID;
            new_rq_obj.tag    = rq_requirement.get_Tag(ReqPro40.enumTagFormat.eTagFormat_FullTag);

            guid_to_obj_dictionary.Add(rq_requirement.GUID, new_rq_obj);

            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;

            // Acquire attributes from ReqPro.
            // Note the use of firstTokenOnly() on those attributes that are freeform text. Those
            // will most likely be stored in EA tagged values, which have a 256 char size limitation.
            // The intention is to limit the attribute to a single line, which hopefully should get it under the
            // size limit. If it does not, the addin will truncate the string and issue a warning when
            // it writes the tagged value.
            if (reqTypeHasOneOrMoreAttrs(new_rq_obj,
               ref hasStatus, ref hasDifficulty, ref hasPriority,
               ref hasSource, ref hasSourceVersion, ref hasSourceSection,
               ref hasSubsystem, ref hasStability, ref hasType))
            {
               if (hasStatus)
               {
                  new_rq_obj.status = rq_requirement.AttrValues[Constants.RP_ATTR_STATUS, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text;
               }
               if (hasDifficulty)
               {
                  new_rq_obj.difficulty = rq_requirement.AttrValues[Constants.RP_ATTR_DIFFICULTY, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text;
               }
               if (hasPriority)
               {
                  new_rq_obj.priority = rq_requirement.AttrValues[Constants.RP_ATTR_PRIORITY, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text;
               }
               if (hasSource)
               {
                  new_rq_obj.source = firstTokenOnly( rq_requirement.AttrValues[Constants.RP_ATTR_SOURCE, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text );
               }
               if (hasSourceVersion)
               {
                  new_rq_obj.sourceVersion = firstTokenOnly( rq_requirement.AttrValues[Constants.RP_ATTR_SOURCE_VERSION, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text );
               }
               if (hasSourceSection)
               {
                  new_rq_obj.sourceSection = firstTokenOnly( rq_requirement.AttrValues[Constants.RP_ATTR_SOURCE_SECTION, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text );
               }
               if (hasSubsystem)
               {
                  new_rq_obj.subsystem = firstTokenOnly( rq_requirement.AttrValues[Constants.RP_ATTR_SUBSYSTEM_TYPE, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text );
               }
               if (hasStability)
               {
                  new_rq_obj.stability = firstTokenOnly( rq_requirement.AttrValues[Constants.RP_ATTR_STABILITY, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text );
               }
               if (hasType)
               {
                  new_rq_obj.type = firstTokenOnly( rq_requirement.AttrValues[Constants.RP_ATTR_REQ_TYPE, ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text );
               }
            }

            new_rq_obj.version = rq_requirement.VersionNumber;
            new_rq_obj.versionDateTime = rq_requirement.VersionDateTime;
            new_rq_obj.iKey = rq_requirement.Key;


            // requirements can trace to other requirements, so we have to find those in order to re-construct
            // that traceability later on. Currently, we only process TracesTo relationships from ReqPro. This
            // seems ok because if a ReqPro user creates a TracesFrom relationship, it shows up as a TracesTo 
            // relationship as well - it just depends on which element (src or dest) you are looking at to see 
            // which type of relationship.
            int limit_numberOfTracesTo = 0;
            if (true == rq_requirement.get_HasTracesTo(ref limit_numberOfTracesTo))
            {
               // scan through the TracesTo relationships
               ReqPro40.Relationships theseRelationships = (ReqPro40.Relationships)rq_requirement.TracesTo;

               int i_numberOfTracesTo;
               theseRelationships.MoveFirst();
               for (i_numberOfTracesTo = 0; i_numberOfTracesTo < limit_numberOfTracesTo; i_numberOfTracesTo++)
               {
                  // Obtain the traced-to requirement from the relationship, and parse it
                  ReqPro40.Relationship thisRelationship = theseRelationships.GetCurrentRelationship();

                  ReqPro40.Requirement tracedToRequirement =
                     thisRelationship.get_DestinationRequirement(ReqPro40.enumRequirementsWeights.eReqWeight_Heavy);

                  if (tracedToRequirement != null)
                  {
                     // Add the GUID of the traced-to requirement to the relevant list within the
                     // object representing the traced-from requirement (ie. parent requirement).
                     new_rq_obj.ReqPro_traces.Add(tracedToRequirement.GUID);
                  }

                  theseRelationships.MoveNext();
               }
            }
         }
         else if (rq_package != null)
         {
            
            new_rq_obj = new ReqPro_object();

            new_rq_obj.isPackage = true;

            // Packages in ReqPro may be prefixed by a number to force ReqPro's alphanumeric sorting
            // algorithm to order the packages in the way the user wants, as dictated by the actual
            // numbers used. EA does not have this problem because it uses a tree position number to
            // control a package/element's position in the project browser. So, strip off any leading
            // numeric from the ReqPro packages.
            string trimstring = " 0123456789";
            char[] trimmer = trimstring.ToCharArray();
            string filtered_name = rq_package.Name.TrimStart(trimmer);

            new_rq_obj.name = filtered_name;
            new_rq_obj.guid = rq_package.GUID;
            new_rq_obj.iKey = rq_package.Key;

            guid_to_obj_dictionary.Add(rq_package.GUID, new_rq_obj);
         }

         if (new_rq_obj != null)
         {

            new_rq_obj.level = level;
            new_rq_obj.treePos = treePos;

            // attach it to its parent object
            ReqPro_object parent_rq_obj = (ReqPro_object)rq_objs.Peek();
            parent_rq_obj.ReqPro_objects.Add( new_rq_obj );
            new_rq_obj.parent = parent_rq_obj;

            // keep a count of the number of requirements the object has beneath it
            if (true == new_rq_obj.isRequirement)
               parent_rq_obj.numberOfRequirements++;

            // push the new object onto the stack, ready for any sub-objects that may belong to it.
            // If, the next time we enter this method, the level is the same, this will get popped off.
            // If, the next time we enter this method, the level is lower, this and possibly more will
            // get popped off.
            rq_objs.Push(new_rq_obj);
            ea_treePos.Push(0);
         }

         // capture what the hierarchy level is for the object just processed.
         lastLevel = level;
      }


      private string firstTokenOnly(string s)
      {
         if (s != null)
         {
            char [] delim = {'\r'};
            string [] tokens = s.Split(delim,2);
            return tokens[0];
         }
         return s;
      }

      #endregion

      #region Trace Relationship Methods

      protected int write_traces(int totalRequirements)
      {
         trace_log = new ArrayList();

         Main.WriteSeperator();
         Main.WriteOutput(string.Format("Writing Trace Information for {0} requirements", totalRequirements), -1);
         int numberWritten = 0;

         // adjust modulo for logging purposes so that the number of output messages is restricted
         // for larger and larger numbers of requirements
         if (totalRequirements > 1000)
            writeTracesModulo = 100;
         else if (totalRequirements > 500)
            writeTracesModulo = 50;
         else if (totalRequirements > 100)
            writeTracesModulo = 20;
         else if (totalRequirements > 50)
            writeTracesModulo = 10;
         else if (totalRequirements > 20)
            writeTracesModulo = 5;
         else // 10 or less
            writeTracesModulo = 1;

         foreach( ReqPro_object sub_obj in rq_root_package.ReqPro_objects )
         {
            if (Main.mustAbort)
               return numberWritten;

            numberWritten = write_traces(sub_obj, numberWritten, totalRequirements);
         }

         Main.WriteOutput("Traces Completed", -1);

         if (trace_log.Count > 0)
         {
            DialogResult dlgRes = MessageBoxEx.Show("Display Log of Trace Connection Changes", "Confirm", MessageBoxButtons.YesNo);
            if (dlgRes == DialogResult.Yes)
            {
               foreach (string s in trace_log)
               {
                  Main.WriteOutput(s, -1);
               }
            }
         }

         Main.WriteSeperator();

         return numberWritten;
      }


      /// <summary>
      /// This method examines all of the ReqPro_object trace relationships and mirrors them in
      /// the EA requirement elements that have been formed from each un-filtered ReqPro_objects.
      /// </summary>
      /// <param name="ea_repository"></param>
      /// <param name="rq_obj"></param>
      private int write_traces(ReqPro_object rq_obj, int numberWritten, int totalRequirements)
      {
         if (Main.mustAbort)
            return numberWritten;

         if (rq_obj.isRequirement)
         {
            // if this object had an EA element made for it during the write_ea_database() process...
            if (rq_obj.ea_element_ID != -1)
            {
               // trace output
               numberWritten++;
               if ((numberWritten % writeTracesModulo) == 0)
               {
                  Main.WriteOutput(string.Format("   {0} of {1}", numberWritten, totalRequirements), -1);
               }

               // Get the EA element the object refers to
               EA.Element ea_req = Main.EA_Repository.GetElementByID(rq_obj.ea_element_ID);
               if (ea_req != null)
               {
                  if (ea_req.Connectors.Count != 0 || rq_obj.ReqPro_traces.Count != 0)
                     create_or_update_connections(ea_req, rq_obj.ReqPro_traces);
               }
            }
         }

         // recurse to ensure we examine the entire hiearchy
         foreach(ReqPro_object sub_obj in rq_obj.ReqPro_objects)
         {
            if (Main.mustAbort)
               break;

            numberWritten = write_traces(sub_obj, numberWritten, totalRequirements);
         }
         return numberWritten;
      }


      /// <summary>
      /// This algorithm is intended to create or delete requirement to requirement relationships
      /// based upon the view of the relationship captured from ReqPro. This function processes
      /// just one element, and so it should be called repeatedly as the facsimile of the ReqPro
      /// database captured in memory as a tree structure, is parsed (see write_traces method).
      /// </summary>
      /// <param name="src_element"></param>
      /// <param name="guids"></param>
      protected void create_or_update_connections(EA.Element src_element, ArrayList guids)
      {
         bool connectorCollectionNeedsRefreshing = false;

         string src_tag = EA_TaggedValues.Read(src_element, Constants.TAG_TAG);
         string src_guid = EA_TaggedValues.Read(src_element, Constants.TAG_GUID, "");

         ReqPro40.Requirement rp_req = null;

         // Create a list of booleans that will allow us to track which elements in the guids
         // list are validated.
         ArrayList traceValidated = null;

         if (guids.Count > 0)
         {
            traceValidated = new ArrayList();
            for (int i = 0; i < guids.Count; i++)
            {
               traceValidated.Add(0);  // initially, none of the guids are validated.
            }
         }

         int i_traceValidated = 0;

         int numberOfconnections = src_element.Connectors.Count;
         if (numberOfconnections > 10)
         {
            Main.WriteOutput(string.Format("   ...processing requirement with large number ({0}) of traces", numberOfconnections), src_element.ElementID);
         }

         // scan the EA elements connectors
         short i_c = 0;
         foreach(EA.Connector c in src_element.Connectors)
         {
            int destId = -1;

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

            // and make sure we filter out self-referential connectors
            if (destId != src_element.ElementID)
            {
               // Get the target element and obtain the tagged values that will effectively mark it as
               // being a requirement 
               EA.Element ea_tgt_req = Main.EA_Repository.GetElementByID(destId);
               if (ea_tgt_req != null && ea_tgt_req.Type.Equals("Requirement"))
               {
                  // Get the GUID from the referenced element
                  string rp_tgt_req_guid = EA_TaggedValues.Read(ea_tgt_req, Constants.TAG_GUID, "");
                  string rp_tgt_req_tag = EA_TaggedValues.Read(ea_tgt_req, Constants.TAG_TAG, "");

                  if (rp_tgt_req_guid != null && rp_tgt_req_guid.Length > 0 && rp_tgt_req_guid.StartsWith("{")
                     && rp_tgt_req_tag != null && rp_tgt_req_tag.Length > 0)
                  {
                     // looks like an EA element that represents a ReqPro requirement

                     // For this source and destination pair, look for evidence that the relationship
                     // is required by examining the guids list passed in as parameter.
                     i_traceValidated = 0;
                     bool validated = false;
                     foreach (string guid in guids)
                     {
                        // Get the target object of the trace relationship
                        ReqPro_object tgt_obj = guid_to_obj_dictionary[guid];
                        if (tgt_obj != null)
                        {
                           if (tgt_obj.ea_element_ID != -1)
                           {
                              if (destId == tgt_obj.ea_element_ID)
                              {
                                 validated = true;

                                 traceValidated[i_traceValidated] = (int)traceValidated[i_traceValidated] + 1;

                                 if ((int)traceValidated[i_traceValidated] > 1)
                                 {
                                    // this is a duplicate trace relationship, so remove it
                                    src_element.Connectors.DeleteAt(i_c, false);
                                    connectorCollectionNeedsRefreshing = true;

                                    trace_log.Add("Deleted duplicate connection between " + src_tag + " and " + rp_tgt_req_tag);
                                 }
                                 break;
                              }
                           }
                        }
                        i_traceValidated++;
                     }

                     if (false == validated)
                     {
                        // We did not find evidence that the trace was needed, so remove it
                        // but we have to check that the object at the other end of the relationship does 
                        // not have a trace back to us first. In EA, we dont care about directionality since
                        // regardless of direction, all relationships exist in the same collection, but
                        // this is not so in ReqPro, which has TracesTo and TracesFrom relationships (and
                        // other types too).
                        // Also, if the target is not a reqpro requirement then we should probably leave the relationship
                        // in place since the target may be an element an EA user is preparing to be a ReqPro requirement
                        // but they have yet to export it.
                        ReqPro_object tgt_obj = guid_to_obj_dictionary[rp_tgt_req_guid];
                        if (tgt_obj != null)
                        {
                           if (false == tgt_obj.ReqPro_traces.Contains(src_guid))
                           {
                              // Does the current requirement we are dealing with have a relationship with another
                              // requirement with the GUID indicated by the tgt_obj ? If is does, then we cannot
                              // delete the relationship in EA. We have to go to the ReqPro database to ascertain this
                              // since the filter dialog may have filtered out the target object and so our local
                              // structure cannot tell us much in this regard.
                              if (rp_req == null)
                                 rp_req = ReqProDatabase.get_requirement_by_guid(src_guid);

                              if (rp_req != null && false == ReqProDatabase.any_relationship_exists(rp_req, tgt_obj.guid))
                              {
                                 src_element.Connectors.DeleteAt(i_c, false);
                                 connectorCollectionNeedsRefreshing = true;

                                 trace_log.Add("Deleted connection between " + src_tag + " and " + rp_tgt_req_tag);
                              }
                           }
                        }
                     }
                  }
               }
            }
            else
            {
               // Found a self-referential requirement - this is not really allowed, so delete the connection
               src_element.Connectors.DeleteAt(i_c, false);
               trace_log.Add("Deleted self-referential connection in " + src_tag);
               connectorCollectionNeedsRefreshing = true;
            }

            i_c++;

            if ((i_c % 10) == 0)
            {
               Main.WriteOutput(string.Format("   ...{0} of {1}", (int)i_c, numberOfconnections), src_element.ElementID);
            }
         }

         // Now look for all guids that have not been marked as validated. For these, a new connection
         // must be established
         i_traceValidated = 0;
         foreach (string guid in guids)
         {
            if ((int)traceValidated[i_traceValidated] == 0)
            {
               // Get the target object of the trace relationship
               ReqPro_object tgt_obj = guid_to_obj_dictionary[guid];
               if (tgt_obj != null)
               {
                  if (tgt_obj.ea_element_ID != -1)
                  {
                     EA.Element ea_tgt_req = Main.EA_Repository.GetElementByID(tgt_obj.ea_element_ID);
                     if (ea_tgt_req != null && ea_tgt_req.Type.Equals("Requirement"))
                     {
                        string rp_tgt_req_tag = EA_TaggedValues.Read(ea_tgt_req, Constants.TAG_TAG, "");

                        // Add the new connection between the src_element and dest_element
                        EA.Connector c = (EA.Connector)src_element.Connectors.AddNew("", "Dependency");
                        c.SupplierID = ea_tgt_req.ElementID;
                        c.Direction = "Source -> Destination";
                        if (false == c.Update())
                        {
                           Main.WriteOutput("New Connector Error : " + c.GetLastError(), ea_tgt_req.ElementID );
                           Main.WriteOutput("...Failed to create connection between " + src_tag + " and " + rp_tgt_req_tag, ea_tgt_req.ElementID);
                           connectorCollectionNeedsRefreshing = true;
                        }
                        else
                        {
                           trace_log.Add("Created connection between " + src_tag + " and " + rp_tgt_req_tag);
                        }
                     }
                  }
               }
            }

            i_traceValidated++;
         }

         if (connectorCollectionNeedsRefreshing)
            src_element.Connectors.Refresh();
      }

 
      #endregion


      /// <summary>
      /// A method to contain common pre-parsing steps.
      /// </summary>
      private void pre_parsing()
      {
         // create an object to represent the root of the database so that we can collect
         // sub-objects (packages or requirements) underneath it.
         rq_root_package = new ReqPro_object();

         rq_root_package.name = "ROOT"; // may be overriden later

         // initialise the ReqPro database hierarchy tracking data
         rq_objs.Clear();
         rq_objs.Push(rq_root_package);
         ea_treePos.Clear();
         ea_treePos.Push(0);
         lastLevel = 0;
      }


        }



   public class ReqProObject_Dictionary : DictionaryBase
   { 

      public ReqPro_object this[string guid]
      {
         get {return (ReqPro_object) this.Dictionary[guid]; }

         set { this.Dictionary[guid] = value; } 
      }

      public void Add(string guid, ReqPro_object rp_obj) 
      { 
         this.Dictionary.Add(guid, rp_obj); 
      } 

      public bool Contains(string guid)
      {
         return this.Dictionary.Contains(guid);
      }


      public ICollection Keys
      {
         get {return this.Dictionary.Keys;}
      }
   }
}