Subversion Repositories DevTools

Rev

Rev 2161 | 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>
        /// The ReqProParser class encapsulates a parsing algorithm for a ReqPro database
        /// designed to pick out packages and requirement elements and to provide that information
        /// to a client with sufficient information to allow it to build a model of that ReqPro
        /// database content in memory, maintaining the hierarchy and ordering of objects found.
        /// </summary>
        public class ReqProParser
        {
      // lists of meta-data in the ReqPro database
      protected ArrayList rq_req_types;
      protected ArrayList rq_req_status_types;
      protected ArrayList delayedMessages;

      // Flag to allow a client to configure this class to import structure only (no requirements -
      // just packages).
      protected bool structure_only = false;


      // constructor
                public ReqProParser()
                {
         delayedMessages = new ArrayList();
                }

      /// <summary>
      /// The parser will call this virtual method when it has opened a ReqPro project
      /// </summary>
      /// <param name="rq_project"></param>
      protected virtual void provideReqProDatabaseInfo( 
         ReqProDB_Artifact rq_artifact,
         EA.Element rq_element)
      {
      }

      /// <summary>
      /// The parser will call this virtual method for every package it finds
      /// in the ReqPro database.
      /// </summary>
      /// <param name="level"></param>
      /// <param name="ea_repository"></param>
      /// <param name="rq_project"></param>
      /// <param name="rq_Package"></param>
      protected virtual void processPackage(int level,
                                            ReqPro40.Package rq_package)
      {
      }

      /// <summary>
      /// The parser will call this virtual method for every requirement it
      /// finds in the ReqPro database.
      /// </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 virtual void processRequirement(int level,
                                                ReqPro40.Package rq_package,
                                                ReqPro40.Requirement rq_requirement)
      {
      }

      /// <summary>
      /// The parser will call this virtual method for every package or requirement it
      /// finds in the ReqPro database.
      /// </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 virtual void processObject(int level,
                                           ReqPro40.Package rq_package,
                                           ReqPro40.Requirement rq_requirement)
      {
      }

      protected void writeDelayedMessages()
      {
         if (delayedMessages.Count > 0)
         {
            foreach(string s in delayedMessages)
            {
               Main.WriteOutput(s, -1);
            }
            Main.WriteSeperator();
         }
      }

      #region Requirement Type methods

      /// <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>
      protected void get_rq_req_types_from_database()
      {
         rq_req_types = new ArrayList();

         ReqPro40.ReqTypes rqtypes = ReqProDatabase.get_requirement_types();
         if (rqtypes != null)
         {
            foreach (ReqPro40.ReqType rq_type in rqtypes)
            {
               ReqPro_ReqType new_req_type = new ReqPro_ReqType(rq_type);
               new_req_type.name     = rq_type.Name;
               new_req_type.prefix   = rq_type.ReqPrefix;
               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;
      }


      protected bool reqTypeHasOneOrMoreAttrs(ReqPro_object rq_obj, 
         ref bool hasStatus, 
         ref bool hasDiff, 
         ref bool hasPrio,
         ref bool hasSource,
         ref bool hasSourceVersion,
         ref bool hasSourceSection,
         ref bool hasSubsystem,
         ref bool hasStability,
         ref bool hasType)
      {
         foreach (ReqPro_ReqType rp_rt in rq_req_types)
         {
            if (rq_obj.tag.StartsWith(rp_rt.prefix))
            {
               hasStatus = rp_rt.hasStatus;
               hasDiff = rp_rt.hasDifficulty;
               hasPrio = rp_rt.hasPriority;
               hasSource = rp_rt.hasSource;
               hasSourceVersion = rp_rt.hasSourceVersion;
               hasSourceSection = rp_rt.hasSourceSection;
               hasSubsystem = rp_rt.hasSubsystem;
               hasStability = rp_rt.hasStability;
               hasType = rp_rt.hasType;

               return hasStatus | hasDiff | hasPrio | hasSource | hasSourceVersion | hasSourceSection | hasSubsystem | hasStability | hasType;
            }
         }

         return false;
      }


      protected bool reqTypeHasOneOrMoreAttrs(string tag_prefix, 
         ref bool hasStatus, 
         ref bool hasDiff, 
         ref bool hasPrio,
         ref bool hasSource,
         ref bool hasSourceVersion,
         ref bool hasSourceSection,
         ref bool hasSubsystem,
         ref bool hasStability,
         ref bool hasType)
      {
         foreach (ReqPro_ReqType rp_rt in rq_req_types)
         {
            if (tag_prefix.StartsWith(rp_rt.prefix))
            {
               hasStatus = rp_rt.hasStatus;
               hasDiff = rp_rt.hasDifficulty;
               hasPrio = rp_rt.hasPriority;
               hasSource = rp_rt.hasSource;
               hasSourceVersion = rp_rt.hasSourceVersion;
               hasSourceSection = rp_rt.hasSourceSection;
               hasSubsystem = rp_rt.hasSubsystem;
               hasStability = rp_rt.hasStability;
               hasType = rp_rt.hasType;

               return hasStatus | hasDiff | hasPrio | hasSource | hasSourceVersion | hasSourceSection | hasSubsystem | hasStability | hasType;
            }
         }

         return false;
      }
      #endregion

      #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>
      protected 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.
         foreach (ReqPro_ReqType rp_rt in rq_req_types)
         {
            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_STATUS, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasStatus = true;

               foreach (ReqPro40.ListItem listItem in attr.ListItems)
               {
                  if (!status_values.Contains(listItem.Text))
                  {
                     status_values.Add(listItem.Text);
                  }
               }
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no Status attribute", rp_rt.prefix) );
            }

            //Main.WriteOutput("REQ_TYPE: " + rp_rt.name, -1);
            //foreach (ReqPro40.Attr a in rp_rt.rq_type.Attrs)
            //{
            //   Main.WriteOutput("   ATTR LABEL: " + a.Label, -1);
            //}

            // Also check if this requirement type has any of the other attributes we might import. This is
            // a little off topic for this function but this is a convenient place to do this until another
            // function is written.
            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_DIFFICULTY, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasDifficulty = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_DIFFICULTY) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_PRIORITY, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasPriority = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_PRIORITY) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_SOURCE, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasSource = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_SOURCE) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_SOURCE_VERSION, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasSourceVersion = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_SOURCE_VERSION) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_SOURCE_SECTION, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasSourceSection = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_SOURCE_SECTION) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_SUBSYSTEM_TYPE, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasSubsystem = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_SUBSYSTEM_TYPE) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_STABILITY, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasStability = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_STABILITY) );
            };

            try 
            {
               ReqPro40.Attr attr = rp_rt.rq_type.get_Attr(Constants.RP_ATTR_REQ_TYPE, ReqPro40.enumAttrLookups.eAttrLookups_Label);
               rp_rt.hasType = true;
            }
            catch 
            {
               delayedMessages.Add( string.Format("WARNING, ReqPro requirement type ({0}) has no {1} attribute", rp_rt.prefix, Constants.RP_ATTR_REQ_TYPE) );
            };
         }

         // if reqpro had no status values, then add a dummy one
         if (status_values.Count == 0)
         {
            status_values.Add("Approved");
         }

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


      /// <summary>
      /// Initiates a ReqPro database parsing operation using a prompted reqpro database open operation
      /// </summary>
      /// <param name="ea_repository"></param>
      /// <returns></returns>
      public virtual bool prompt_and_parse(ReqProDB_Artifact.MODE mode, out bool cancelled)
      {
         cancelled = false;

         try
         {
            // If we can find a ReqProDB artifact in the package, use it to logon to the ReqPro database
            ReqProDB_Artifact RQ_Artifact = new ReqProDB_Artifact();
            EA.Element RQ_Element = RQ_Artifact.get_rq_artifact();
            if (RQ_Element != null)
            {
               // check to see if the ReqProDB element has the same mode as that specified by parameter
               // however bypass the validation check if the mode of the artifact is undefined or the mode
               // of the current operation is "export".
               ReqProDB_Artifact.MODE rq_art_mode = RQ_Artifact.get_mode(RQ_Element);
               if (rq_art_mode != ReqProDB_Artifact.MODE.UNDEFINED)
               {
                  if (mode != ReqProDB_Artifact.MODE.EXPORT && rq_art_mode != mode)
                  {
                     MessageBoxEx.Show(
                        "You are attempting to use a ReqProDB element\n" +
                        "and associated EA model area for a different\n" +
                        "purpose than that which was specified when the\n" +
                        "element was first created.",
                        "Error");
                     cancelled = true;
                     return false;
                  }
               }

               if (Main.mustAbort)
                  return false;

               if (false == RQ_Artifact.OpenReqProProject(RQ_Element, out cancelled))
                  return false;

               if (cancelled == true)
                  return  false;
            }
            
            if (Main.mustAbort)
               return false;

            // If no ReqProDB artifact found, prompt user to logon to the ReqPro database
            if (false == ReqProDatabase.opened())
            {
               DialogResult dlgRes;

               // Confirm user wants to create a new reqpro import/export area.
               dlgRes = MessageBoxEx.Show(
                  "The area of EA model structure you have selected,\n" +
                  "does not appear to be part of an existing ReqPro\n" +
                  "import/export area.\n\n" +
                  "Do you wish to configure it for ReqPro import/export?\n\n" +
                  "If you choose YES, you will be asked to select a\n" +
                  "ReqPro database within the filesystem to associate\n" +
                  "with this EA model area.\n" +
                  "Choosing NO will cancel the operation.", 
                  "Confirm", MessageBoxButtons.YesNo);
               if (dlgRes == DialogResult.No)
               {
                  cancelled = true;
                  return false;
               }

               // let user select the ReqPro database file name
               OpenFileDialog ofd = new OpenFileDialog();
               ofd.Title = "Select Requisite Pro project file";
               ofd.Filter = "ReqPro files (*.rqs)|*.rqs|All files (*.*)|*.*";
               dlgRes = ofd.ShowDialog();
               if (dlgRes != DialogResult.OK)
                  return false;

               if (Main.mustAbort)
                  return false;

               // let user specifiy username/password
               Logon logon = new Logon("");
               dlgRes = logon.ShowDialog();
               if (dlgRes != DialogResult.OK)
                  return false;

               if (Main.mustAbort)
                  return false;

               string username = logon.ebUserName.Text;
               string password = logon.ebPassword.Text;

               // Connect to the ReqPro database using the ReqPro extensibility mechanism
               ReqProDatabase.open(ofd.FileName, username, password);

               if (Main.mustAbort)
                  return false;

               // if we do not currently have a ReqProDB artifact, create one
               if (RQ_Element == null)
               {
                  RQ_Element = RQ_Artifact.create_rq_artifact(
                     EA_ProjectBrowser.get_selected_package(), 
                     ReqProDatabase.name(), 
                     ReqProDatabase.description(), 
                     logon.ebUserName.Text, 
                     ofd.FileName, 
                     ReqProDatabase.guid());
               }
               if (Main.mustAbort)
                  return false;
            }

            if (Main.mustAbort)
               return false;

            // Now do the parsing of the ReqPro database
            if (true == ReqProDatabase.opened())
            {
               // give req pro project object to user of this class
               provideReqProDatabaseInfo(RQ_Artifact, RQ_Element);

               // get the requirement types from the req pro project
               get_rq_req_types_from_database();

               get_rq_req_status_types_from_database();

               // Get the ReqPro root package and parse it
               Main.WriteOutput("Acquiring ReqPro Database", -1);

               parseRootPackage(0, ReqProDatabase.get_root_package());

               Main.WriteSeperator();

               if (Main.mustAbort)
                  return false;

               return true;
            }
         }
         catch (Exception ex)
         {
            Main.MessageBoxException(ex, "Exception (prompt_and_parse)");
            ReqProDatabase.close();
         }  
         return false;
      }

      /// <summary>
      /// This method handles the parsing of the root package in the ReqPro database.
      /// The method is almost identical to the parsePackage method, except that this
      /// deals with a ReqPro40.RootPackage object, whereas the latter deals with a
      /// ReqPro40.Package object. Perhaps the two functions could be combined into 
      /// one if we move to .NET 2 where c# has some support for generic (template)
      /// programming.
      /// </summary>
      /// <param name="repository"></param>
      /// <param name="rq_project"></param>
      /// <param name="thisPackage"></param>
      private void parseRootPackage(int level,
                                    ReqPro40.RootPackage thisPackage)
      {
         // Scan through the sub-packages of the root package
         int limit_packageCount = thisPackage.get_Count(ReqPro40.enumElementTypes.eElemType_Package);
         if (limit_packageCount > 0)
         {
            int i_packageCount;
            thisPackage.MoveFirst();
            for (i_packageCount = 0; i_packageCount < limit_packageCount; i_packageCount++)
            {
               if (Main.mustAbort)
                  break;

               // Read the sub-package and parse it
               ReqPro40.Package subPackage = (ReqPro40.Package)thisPackage.GetCurrentElement();
               parsePackage(level+1, "", subPackage);
               thisPackage.MoveNext();
            }
         }

         if (Main.mustAbort)
            return;

         if (structure_only == false)
         {
            // Scan through the requirements directly beneath the root package
            int limit_reqCount = thisPackage.get_Count(ReqPro40.enumElementTypes.eElemType_Requirement);
            if (limit_reqCount > 0)
            {
               // Obtain the requirement element key list from the root package and scan through each entry
               int i_reqCount;
               System.Object[,] keyList = (System.Object[,])thisPackage.KeyList(ReqPro40.enumElementTypes.eElemType_Requirement);
               for (i_reqCount = 0; i_reqCount < limit_reqCount; i_reqCount++)
               {
                  if (Main.mustAbort)
                     break;

                  // Obtain the ReqPro requirement from its key, and parse it
                  ReqPro40.Requirement thisRequirement = ReqProDatabase.get_requirement_by_key(keyList[0,i_reqCount]);
                  if (thisRequirement != null)
                  {
                     parseRequirement(level+1, null, thisRequirement);                  
                  }
               }
            }
         }
      }

      /// <summary>
      /// This method handles the parsing of each package beneath the root package
      /// in the ReqPro database.
      /// The method is almost identical to the parseRootPackage method, except that 
      /// this deals with a ReqPro40.Package object, whereas the latter deals with a
      /// ReqPro40.RootPackage object. Perhaps the two functions could be combined into 
      /// one if we move to .NET 2 where c# has some support for generic (template)
      /// programming.      
      /// </summary>
      /// <param name="repository"></param>
      /// <param name="rq_project"></param>
      /// <param name="thisPackage"></param>
      private void parsePackage(int level,
                                string prefix,
                                ReqPro40.Package thisPackage)
      {
         if (Main.mustAbort)
            return;

         Main.WriteOutput(prefix + thisPackage.Name, -1);

         // call user defined functions
         processPackage(level, thisPackage);
         processObject(level, thisPackage, null);

         // Scan through the sub-packages of this package
         int limit_packageCount = thisPackage.get_Count(ReqPro40.enumElementTypes.eElemType_Package);
         if (limit_packageCount > 0)
         {
            int i_packageCount;
            thisPackage.MoveFirst();
            for (i_packageCount = 0; i_packageCount < limit_packageCount; i_packageCount++)
            {
               if (Main.mustAbort)
                  break;

               // Read the sub-package and parse it
               ReqPro40.Package subPackage = (ReqPro40.Package)thisPackage.GetCurrentElement();
               parsePackage(level+1, prefix + "    ", subPackage);
               thisPackage.MoveNext();
            }
         }

         if (Main.mustAbort)
            return;

         if (structure_only == false)
         {
            // Scan through the requirements directly beneath this package
            int limit_reqCount = thisPackage.get_Count(ReqPro40.enumElementTypes.eElemType_Requirement);
            if (limit_reqCount > 0)
            {
               // Obtain the requirement element key list from this package and scan through each entry
               int i_reqCount;
               System.Object[,] keyList = (System.Object[,])thisPackage.KeyList(ReqPro40.enumElementTypes.eElemType_Requirement);
               for (i_reqCount = 0; i_reqCount < limit_reqCount; i_reqCount++)
               {
                  if (Main.mustAbort)
                     break;

                  // Obtain the ReqPro requirement from its key, and parse it
                  ReqPro40.Requirement thisRequirement = ReqProDatabase.get_requirement_by_key(keyList[0,i_reqCount]);
                  if (thisRequirement != null)
                  {
                     parseRequirement(level+1, thisPackage, thisRequirement);                  
                  }
               }
            }
         }
      }



      /// <summary>
      /// This method parses a requirement and any sub-requirements found in the 
      /// ReqPro database by the package/root package parsers.
      /// NOTE that when called from the parseRootPackage method, the thisPackage
      /// parameter will be null.
      /// </summary>
      /// <param name="repository"></param>
      /// <param name="rq_project"></param>
      /// <param name="thisPackage"></param>
      /// <param name="thisRequirement"></param>
      private void parseRequirement(int level,
                                    ReqPro40.Package thisPackage, 
                                    ReqPro40.Requirement thisRequirement)
      {
         if (Main.mustAbort)
            return;

         // call user defined functions
         processRequirement(level, thisPackage, thisRequirement);
         processObject(level, thisPackage, thisRequirement);

         // requirements can have children that are requirements, so we have to find those
         int limit_numberOfChildren = 0;
         if (true == thisRequirement.get_HasChildren(ref limit_numberOfChildren))
         {
            // scan through the child relationships
            ReqPro40.Relationships theseRelationships = (ReqPro40.Relationships)thisRequirement.Children;

            int i_numberOfChildren;
            theseRelationships.MoveFirst();
            for (i_numberOfChildren = 0; i_numberOfChildren < limit_numberOfChildren; i_numberOfChildren++)
            {
               if (Main.mustAbort)
                  break;

               // Obtain the sub-requirement from the relationship, and parse it
               ReqPro40.Relationship thisRelationship = theseRelationships.GetCurrentRelationship();

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

               if (subRequirement != null)
               {
                  parseRequirement(level+1, thisPackage, subRequirement);
               }

               theseRelationships.MoveNext();
            }
         }
      }

        }


 

}