Subversion Repositories DevTools

Rev

Rev 2145 | Rev 2149 | 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>
        /// Summary description for CopyReqProDatabaseToMemory.
        /// </summary>
        public class CopyReqProDatabaseToMemory : ReqProParser
        {
      protected bool allowPackageStructureFragments = false;

      protected EA_Utilities EA_Utils;

      // items setup by base class calling into provideReqProDatabaseInfo()
      protected ReqPro40.Project RQ_project;
      protected ReqPro40.Application RQ_app;
      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

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

      protected ArrayList rq_req_types;
      protected ArrayList rq_req_status_types;


      /// <summary>
      /// Constructor logic
      /// </summary>
      /// <param name="ea_repository"></param>
                public CopyReqProDatabaseToMemory(EA.Repository ea_repository): base()
                {
         try
         {
            // create an EA_Utilities object
            EA_Utils = new EA_Utilities(ea_repository);

            // figure out where in the EA database we will place the results of parsing
            object o;
            EA.ObjectType type;
            type = ea_repository.GetTreeSelectedItem(out o);
            if (type == EA.ObjectType.otElement)
            {
               ea_rootPackage = ea_repository.GetPackageByID( ((EA.Element)o).PackageID );
            }
            else if (type == EA.ObjectType.otPackage)
            {
               ea_rootPackage = (EA.Package)o;
            }
            else
            {
               Exception e = new Exception("No Root Package given");
               throw e;
            }
         }
         catch (Exception ex)
         {
            MessageBox.Show(ex.Message, "Error (CopyReqProDatabaseToMemory::CopyReqProDatabaseToMemory)", MessageBoxButtons.OK);
         }    
                }



      #region Requirement Status Type methods

      /// <summary>
      /// Get the requirement status types from the ReqPro database, into a simple list, where each element
      /// describes the requirement status type and whether it is filtered or not. This list can be given to 
      /// the ReqProFilterForm to capture the users requirement status type filtering decisions. 
      /// </summary>
      private void get_rq_req_status_types_from_database()
      {
         StringCollection status_values = new StringCollection();

         // Each requirement type can have its own unique list of status attribute values
         // so we have to go through each requirement type and find the set of status values
         // that each one has and add them to out string collection, if the collection does
         // not already have the strings ofcoarse. So, we are merging together all the status
         // values in the ReqPro database, into one set.
         ReqPro40.ReqTypes rqtypes = RQ_project.ReqTypes;
         foreach (ReqPro40.ReqType rq_type in rqtypes)
         {
            ReqPro40.Attr attr = rq_type.get_Attr("Status", ReqPro40.enumAttrLookups.eAttrLookups_Label);
            foreach (ReqPro40.ListItem listItem in attr.ListItems)
            {
               if (!status_values.Contains(listItem.Text))
               {
                  status_values.Add(listItem.Text);
               }
            }
         }

         // With our merged set of status values, create a list of ReqPro_ReqStatus objects.
         rq_req_status_types = new ArrayList();
         foreach (string s in status_values)
         {
            ReqPro_ReqStatus new_ReqPro_ReqStatus = new ReqPro_ReqStatus();
            new_ReqPro_ReqStatus.filtered = false;
            new_ReqPro_ReqStatus.status_value = s;
            rq_req_status_types.Add(new_ReqPro_ReqStatus);
         }
      }

      /// <summary>
      /// Examine the requirement status type list to see if the requirement status type of 
      /// the specified object has been filtered or not.
      /// </summary>
      /// <param name="rq_obj"></param>
      /// <returns></returns>
      protected bool reqStatusTypeIsFiltered(ReqPro_object rq_obj)
      {
         foreach (ReqPro_ReqStatus rqs in rq_req_status_types)
         {
            if (0 == rqs.status_value.CompareTo(rq_obj.status))
            {
               return rqs.filtered;
            }
         }
         return false;
      }

      #endregion

      #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);
            }
         }

      /// <summary>
      /// Get the requirement types from the ReqPro database, into a simple list, where each element
      /// describes the requirement type and whether it is filtered or not. This list can be given to 
      /// the ReqProFilterForm to capture the users requirement type filtering decisions. 
      /// </summary>
      private void get_rq_req_types_from_database()
      {
         rq_req_types = new ArrayList();

         ReqPro40.ReqTypes rqtypes = RQ_project.ReqTypes;
         foreach (ReqPro40.ReqType rq_type in rqtypes)
         {
            ReqPro_ReqType new_req_type = new ReqPro_ReqType();
            new_req_type.name     = rq_type.Name;
            new_req_type.prefix   = rq_type.ReqPrefix;
            new_req_type.filtered = false;
            rq_req_types.Add(new_req_type);
         }
      }

      /// <summary>
      /// Examine the requirement type list to see if the requirement type of the specified object
      /// has been filtered or not.
      /// </summary>
      /// <param name="rq_obj"></param>
      /// <returns></returns>
      protected bool reqTypeIsFiltered(ReqPro_object rq_obj)
      {
         return ((ReqPro_ReqType)rq_req_types[rq_obj.tag_enum]).filtered;
      }

      #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(EA.Repository ea_repository, ReqProDB_Artifact.MODE mode)
      {
         try
         {

            pre_parsing();
            if (true == base.prompt_and_parse(ea_repository, mode))
            {
               // obtain the requirement types from the reqpro database (need these for
               // the filter dialog)
               get_rq_req_types_from_database();
               set_rq_req_types_in_copied_data(rq_root_package);

               get_rq_req_status_types_from_database();

               // bring up the 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;
                  RQ_project.CloseProject();

                  // 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;
               }
               RQ_project.CloseProject();
            }
         }
         catch (Exception ex)
         {
            MessageBox.Show(ex.Message, "Error (CopyReqProDatabaseToMemory::parse)", MessageBoxButtons.OK);
         }      
         return false;
      }
 


      /// <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(ReqPro40.Application rq_app, 
         ReqPro40.Project rq_project, 
         ReqProDB_Artifact rq_artifact,
         EA.Element rq_element)
      {
         RQ_app = rq_app;
         RQ_project = rq_project;
         RQ_Artifact = rq_artifact;
         RQ_Element = rq_element;
      }

      /// <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,
                                            EA.Repository ea_repository, 
                                            ReqPro40.Project rq_project, 
                                            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 = new ReqPro_object();
         if (rq_requirement != null)
         {
            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);

            new_rq_obj.status     = rq_requirement.AttrValues["Status", ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text;
            new_rq_obj.difficulty = rq_requirement.AttrValues["Difficulty", ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text;
            new_rq_obj.priority   = rq_requirement.AttrValues["Priority", ReqPro40.enumAttrValueLookups.eAttrValueLookup_Label].Text;
            new_rq_obj.version    = rq_requirement.VersionNumber;
            new_rq_obj.versionDateTime = rq_requirement.VersionDateTime;


            // 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.
            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.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.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;
      }
      
      #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";

         // 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;
      }


                         

        }
}