Rev 2147 | Rev 2151 | 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 writtenprotected EA.Package ea_rootPackage;// Hierarchy tracking data for the parsing of a ReqPro databaseprotected Stack rq_objs = new Stack(); // Top of stack indicates current parent packageprotected Stack ea_treePos = new Stack(); // Top of stack indicates current tree position reflecting object ordering within the parent packageprotected int lastLevel; // Allows us to determine if we have gone down/up in the hierarchy// Collector for the results of the parsingprotected 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 objectEA_Utils = new EA_Utilities(ea_repository);// figure out where in the EA database we will place the results of parsingobject 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 ){// recurseset_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 copiedReqProFilterForm 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 availableif (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 packageReqPro_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 relationshipsReqPro40.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 itReqPro40.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 objectReqPro_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 itif (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>/// Finds and returns the ReqPro_object instance that contains the specified GUID. The/// GUID is that of the originating ReqPro requirement, which is copied into a ReqPro_object/// instance when it is created. This find operation supports the ability to resolve object to/// object trace relationships mirrored from the ReqPro database during the construction/// of the ReqPro_object hierarchy./// </summary>/// <param name="rq_obj"></param>/// <param name="ReqProGUID"></param>/// <returns></returns>protected ReqPro_object findReqPro_object_byReqProGUID(ReqPro_object rq_obj, string ReqProGUID){if (rq_obj.guid.CompareTo(ReqProGUID) == 0){return rq_obj;}foreach (ReqPro_object sub_rq_obj in rq_obj.ReqPro_objects){ReqPro_object tgt_obj = findReqPro_object_byReqProGUID(sub_rq_obj, ReqProGUID);if (tgt_obj != null)return tgt_obj;}return null;}/// <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>protected void write_traces(EA.Repository ea_repository, ReqPro_object rq_obj){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){EA.Element ea_rq_obj = ea_repository.GetElementByID(rq_obj.ea_element_ID);if (ea_rq_obj != null){foreach(string s in rq_obj.ReqPro_traces){ReqPro_object tgt_obj = findReqPro_object_byReqProGUID(rq_root_package,s);if (tgt_obj != null){if (tgt_obj.ea_element_ID != -1){EA.Element ea_tgt_obj = ea_repository.GetElementByID(tgt_obj.ea_element_ID);if (ea_tgt_obj != null){add_connection(ea_repository, ea_rq_obj, ea_tgt_obj);}}}}}}}// recurse to ensure we examine the entire hiearchyforeach(ReqPro_object sub_obj in rq_obj.ReqPro_objects){write_traces(ea_repository, sub_obj);}}/// <summary>/// Adds a connection between one EA element and another/// </summary>/// <param name="repository"></param>/// <param name="rq_artifact"></param>/// <param name="ea_req"></param>protected void add_connection(EA.Repository ea_repository, EA.Element src_element, EA.Element dest_element){// Add the new requirement to the src_elementEA.Connector c = (EA.Connector)src_element.Connectors.AddNew("", "Relationship");c.SupplierID = dest_element.ElementID;//c.Stereotype = "trace";c.Direction = "Source -> Destination";if (false == c.Update()){ea_repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME, "New Connector Error : " + c.GetLastError(), dest_element.ElementID );}src_element.Connectors.Refresh();}/// <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 datarq_objs.Clear();rq_objs.Push(rq_root_package);ea_treePos.Clear();ea_treePos.Push(0);lastLevel = 0;}}}