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 databaseprotected 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;// constructorpublic 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 oneif (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 databaseReqProDB_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 databaseif (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 nameOpenFileDialog 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/passwordLogon 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 mechanismReqProDatabase.open(ofd.FileName, username, password);if (Main.mustAbort)return false;// if we do not currently have a ReqProDB artifact, create oneif (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 databaseif (true == ReqProDatabase.opened()){// give req pro project object to user of this classprovideReqProDatabaseInfo(RQ_Artifact, RQ_Element);// get the requirement types from the req pro projectget_rq_req_types_from_database();get_rq_req_status_types_from_database();// Get the ReqPro root package and parse itMain.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 packageint 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 itReqPro40.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 packageint 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 entryint 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 itReqPro40.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 functionsprocessPackage(level, thisPackage);processObject(level, thisPackage, null);// Scan through the sub-packages of this packageint 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 itReqPro40.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 packageint 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 entryint 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 itReqPro40.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 functionsprocessRequirement(level, thisPackage, thisRequirement);processObject(level, thisPackage, thisRequirement);// requirements can have children that are requirements, so we have to find thoseint limit_numberOfChildren = 0;if (true == thisRequirement.get_HasChildren(ref limit_numberOfChildren)){// scan through the child relationshipsReqPro40.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 itReqPro40.Relationship thisRelationship = theseRelationships.GetCurrentRelationship();ReqPro40.Requirement subRequirement =thisRelationship.get_DestinationRequirement(ReqPro40.enumRequirementsWeights.eReqWeight_Heavy);if (subRequirement != null){parseRequirement(level+1, thisPackage, subRequirement);}theseRelationships.MoveNext();}}}}}