Rev 2173 | Blame | Compare with Previous | Last modification | View Log | RSS feed
using System;using System.Text;using System.Globalization;using System.Collections;using System.Windows.Forms;using ReqPro40;namespace EA_ReqPro{/// <summary>/// ImportReqProDatabase is a specialisation of CopyReqProDatabaseToMemory, designed to copy the/// ReqPro database content into an EA database, discarding the structure of and hierarchy of/// packages, selecting just the requirements found in the ReqPro database for storage into EA,/// where they will be used for design traceability purposes./// </summary>public class ImportReqProDatabase : CopyReqProDatabaseToMemory{private struct req_lists_t{public ArrayList ea_reqs;public ArrayList ea_GUIDs;public ArrayList new_ea_reqs;public ArrayList new_ea_GUIDs;public ArrayList orphaned_ea_reqs;};private req_lists_t gReq_lists = new req_lists_t();private int totalRequirements = 0;private EA.Package gCurrentPackage = null;private EA.Package gNewReqProDBPackage = null;private EA.Package gChangeHistoryPackage = null;private EA.Package gOrphansPackage = null;private int gNewRequirementCount = 0;private int gModifiedRequirementCount = 0;private int gStatusUpdatedRequirementCount = 0;private int gOrphanedCount = 0;private EA.Element gChangeLog = null;/// <summary>/// Constructor logic/// </summary>/// <param name="ea_repository"></param>public ImportReqProDatabase(): base(){totalRequirements = 0;}/// <summary>/// Method to parse a ReqPro database using the information in a ReqProDB artifact,/// assumed to be selected in EA's project browser prior to the method call./// </summary>/// <param name="ea_repository"></param>/// <returns></returns>public override bool prompt_and_parse(ReqProDB_Artifact.MODE mode, out bool cancelled){cancelled = false;try{base.structure_only = false;string importDateTime = System.DateTime.Now.ToString();Main.WriteOutput("EA Requirement import at " + importDateTime, -1 );Main.WriteOutput("",-1);if (true == base.prompt_and_parse(mode, out cancelled)){if (Main.mustAbort)return false;Main.EA_Repository.EnsureOutputVisible(Main.GUI_OUTPUT_TAB_NAME);// Configure the ReqProDB artifact as a traceability artifactRQ_Artifact.set_traceability_mode(RQ_Element);ImportFromReqPro(importDateTime);return true;}}catch (Exception ex){Main.MessageBoxException(ex, "Exception (parse)");}return false;}/// <summary>/// Displays the content of the change log element to the output trace display. This method must/// be kept in sync with the ImportFromReqPro method, obviously./// </summary>/// <param name="ea_repository"></param>/// <param name="changeLog"></param>public void displayChangeLog(EA.Element changeLog){int numberOfChangesForDeletedElements = 0;Main.WriteOutput("Displaying Import Change Log", -1);if (changeLog.Notes == null || changeLog.Notes.Length == 0 || changeLog.Notes.Trim().Length == 0){Main.WriteOutput(" No changes", -1);}else{// The change log element contains all the log in its notes section. Break it up into// lines, because each line is a single log entry, so that we can process them one at// a time.string delimStr = "\n";char [] delim = delimStr.ToCharArray();string[] log_strings = changeLog.Notes.Split(delim,2000);// modify delimiters to : character so that we can extract the parameters for each log entrydelimStr = ":";delim = delimStr.ToCharArray();foreach(string s in log_strings){if (Main.mustAbort)break;// over time, users may delete the orphaned requirements from the EA database, but their// GUIDs will still exist in the change log. Use of such a GUID will cause// an exception in the GetElementByGuid() function. What should we do? We can attempt// to remove those GUIDs from the list. We can do this by accumulating a new list of GUIDs// that is formed from the old list, where only good items from the old list find their// way into the new list. Gradually, the list is wittled away as EA users delete orphaned// requirements having first dealt with the design change impacts that resulted from their// orphanage.try{if (s.StartsWith("{")){string trimmed_s = s.Trim();// Get log entry;s parameters.string[] parameters = trimmed_s.Split(delim, 10);// The first parameter is the GUID so use it to get the requirement elementEA.Element ea_req = Main.EA_Repository.GetElementByGuid(parameters[0]);// Now discriminate actions based on the second parameterif (parameters[1].StartsWith("Created")){// user may have deleted old orphaned requirements so we would not be able to get the EA// element so we cannot use it for display purposes at all.if (ea_req == null){Main.WriteOutput(" Created : " + parameters[2], -1 );}else{Main.WriteOutput(" Created : " + ea_req.Name, ea_req.ElementID );}}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith("Orphaned")){// user may have deleted old orphaned requirements so we would not be able to get the EA// element so we cannot use it for display purposes at all.if (ea_req == null){Main.WriteOutput(" Orphaned : " + parameters[2], -1 );}else{// We have the EA element so do a full display of the orphaned itemMain.WriteOutput(" Orphaned : " + ea_req.Name, ea_req.ElementID );foreach (EA.Connector theConnector in ea_req.Connectors){int destId = -1;// we dont care about direction of relationship, so test for bothif (theConnector.ClientID == ea_req.ElementID)destId = theConnector.SupplierID;else if (theConnector.SupplierID == ea_req.ElementID)destId = theConnector.ClientID;elsedestId = theConnector.SupplierID;// and make sure we filter out self-referential connectorsif (destId != ea_req.ElementID){EA.Package tgt_pkg = (EA.Package)Main.EA_Repository.GetPackageByID( destId );if (tgt_pkg != null){Main.WriteOutput(" --> " + tgt_pkg.Name, tgt_pkg.PackageID);}else{EA.Element tgt_ele = (EA.Element)Main.EA_Repository.GetElementByID( destId );if (tgt_ele != null){//if (!tgt_ele.Type.StartsWith("Requirement"))Main.WriteOutput(" --> " + tgt_ele.Name, tgt_ele.ElementID);}}}}}}else{if (ea_req != null){/////////////////////////////////////////////////////////////////////////////////////////////////////////////if (parameters[1].StartsWith(Constants.CHG_LOG_NAME)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_NAME, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_NOTES)) // aka Description of the requirement{Main.WriteOutput(string.Format(" Modified {0}: {1}", "Description", ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_DIFFICULTY)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_DIFFICULTY, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_PRIORITY)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_PRIORITY, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_VERSION)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_VERSION, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_STATUS)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_STATUS, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_SUBSYSTEM_TYPE)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_SUBSYSTEM_TYPE, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_STABILITY)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_STABILITY, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_REQ_TYPE)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_REQ_TYPE, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_SOURCE_VERSION)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_SOURCE_VERSION, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_SOURCE_SECTION)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_SOURCE_SECTION, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}/////////////////////////////////////////////////////////////////////////////////////////////////////////////else if (parameters[1].StartsWith(Constants.CHG_LOG_SOURCE)){Main.WriteOutput(string.Format(" Modified {0}: {1}", Constants.CHG_LOG_SOURCE, ea_req.Name), ea_req.ElementID );Main.WriteOutput(" <<< " + parameters[2], ea_req.ElementID);Main.WriteOutput(" >>> " + parameters[3], ea_req.ElementID);}}else{numberOfChangesForDeletedElements++;}}}}catch{}}}if (numberOfChangesForDeletedElements > 0){Main.WriteOutput("NOTE: Some propert changes were not shown because the requirement elements have since been deleted", -1);Main.WriteOutput("and it is impossible to use the change log recorded GUIDs to determine what they were.", -1);}}/// <summary>/// Rid a string of any : char because we use that as a delimiter in the change log and so we/// do not want a users use of the character to confuse the change log display mechanism./// </summary>/// <param name="s"></param>/// <returns></returns>private string NO_COLONS(string s){string sc = s.Replace(':', ' ');sc = sc.Replace('\r' , ' ');sc = sc.Replace('\n' , ' ');return sc;}/// <summary>/// A method that imports requirements from a ReqPro database into EA for the purpose/// of supporting requirement-to-design traceability./// </summary>/// <param name="repository"></param>private void ImportFromReqPro(string importDateTime){try{DialogResult dlgres;// begin to initialise some of our element listsgReq_lists.new_ea_reqs = new ArrayList();gReq_lists.new_ea_GUIDs = new ArrayList();gReq_lists.ea_GUIDs = new ArrayList();gReq_lists.orphaned_ea_reqs = new ArrayList();ArrayList ea_req_matched = new ArrayList();// Obtain the EA parent package so that we can create under it our ReqProDB structure and change history packagesEA.Package parentPackage = Main.EA_Repository.GetPackageByID( base.RQ_Element.PackageID );// Find or create the change history package in which to store change logsgChangeHistoryPackage = find_or_create_change_history_package(parentPackage);// create the ReqProDB structure package in the parent container package, and attach it to its counterpart// in the ReqPro_object tree structure (ie. attach it to the root node therein).gNewReqProDBPackage = (EA.Package)parentPackage.Packages.AddNew( "ReqProDB " + importDateTime, "Package");gNewReqProDBPackage.Update();rq_root_package.ea_element_ID = gNewReqProDBPackage.PackageID;// Migrate the older form of import directory and change log, into the newer formatmigrate_old_import_packages_to_change_history_package(parentPackage);Main.EA_Repository.RefreshModelView(parentPackage.PackageID);if (Main.mustAbort)return;// Create a package to hold orphans. This will be removed later on if it ends up empty.gOrphansPackage = find_or_create_orphans_package(parentPackage);// Get a list of EA requirements that already exist under the overall ReqProDB container packageArrayList allowedElementTypes = new ArrayList();allowedElementTypes.Add("Requirement");allowedElementTypes.Add("UseCase");Main.WriteOutput("Acquiring list of elements already in EA", -1);EA_ElementAccumulator reqLister = new EA_ElementAccumulator(allowedElementTypes);EA_Parsing.findAndProcessPackageElements( parentPackage, reqLister, true );gReq_lists.ea_reqs = reqLister.Elements;if (Main.mustAbort)return;// For the list we just found, get an adjacent list of the associated ReqPro GUIDs.// The two lists will be indexable by the same index.foreach (EA.Element ea_req in gReq_lists.ea_reqs){string GUID = EA_TaggedValues.Read(ea_req, Constants.TAG_GUID);gReq_lists.ea_GUIDs.Add(GUID);ea_req_matched.Add(false);}if (Main.mustAbort)return;// Create the change log element in the change history packagegChangeLog = (EA.Element)gChangeHistoryPackage.Elements.AddNew("Change Log" + " - import " + importDateTime, "InformationItem");gChangeLog.Notes = "";gChangeLog.TreePos = gChangeHistoryPackage.Elements.Count - 1;gChangeLog.Update();// Get a flattened list of requirements from the hierarchical data the user has filteredArrayList rq_req_collection = new ArrayList();get_rq_req_collection(ref rq_req_collection);// initialise some statistical varsgNewRequirementCount = 0;gModifiedRequirementCount = 0;gStatusUpdatedRequirementCount = 0;gOrphanedCount = 0;Main.WriteOutput("Merging ReqPro content to EA", -1);// loop through all of the ReqPro requirements we have captured from the ReqPro databaseforeach(ReqPro_object rq_obj in rq_req_collection){if (Main.mustAbort)break;try{// Find which EA requirement element refers to the current ReqPro GUIDint i_ea_req;i_ea_req = gReq_lists.ea_GUIDs.IndexOf( rq_obj.guid );// If EA element was foundif (0 <= i_ea_req){// update orphanage detection dataea_req_matched[i_ea_req] = true;rq_obj.matched = true;EA.Element ea_req = ((EA.Element)gReq_lists.ea_reqs[i_ea_req]);rq_obj.ea_element_ID = ea_req.ElementID;// Only update the requirement if it was not mastered in EA, or// unrestricted imports have been enabledif ( (false == EA_TaggedValues.Read(ea_req, Constants.TAG_CREATED_IN_EA, Constants.DEFAULT_CREATED_IN_EA))|| (true == EA_TaggedValues.Read(ea_req, Constants.TAG_UNRESTRICTED_IMPORTS, Constants.DEFAULT_UNRESTRICTED_IMPORTS)) ){// This ReqPro requirement already has a counterpart in EA, so we must simply// update the counterpart. But, to aid the designer, we need to try to tell them// how the requirement has changed, which could ofcoarse change the meaning// of the requirement and require design changes to be done.bool meaningMayHaveChanged = false;bool statusMayHaveChanged = false;string ea_req_name = rq_obj.tag + " " + rq_obj.name;Main.WriteOutput("Updating: " + ea_req_name, rq_obj.ea_element_ID);ea_req.Name = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Name, Constants.CHG_LOG_NAME, ea_req_name, ref meaningMayHaveChanged);ea_req.Notes = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Notes, Constants.CHG_LOG_NOTES, rq_obj.text, ref meaningMayHaveChanged);ea_req.Difficulty = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Difficulty, Constants.CHG_LOG_DIFFICULTY, rq_obj.difficulty, ref statusMayHaveChanged);ea_req.Priority = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Priority, Constants.CHG_LOG_PRIORITY, rq_obj.priority, ref statusMayHaveChanged);ea_req.Status = updated_property_value_if_changed(gChangeLog, ea_req, ea_req.Status, Constants.CHG_LOG_STATUS, rq_obj.status, ref statusMayHaveChanged);if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SOURCE, Constants.CHG_LOG_SOURCE, rq_obj.source, ref statusMayHaveChanged);if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SOURCE_VERSION, Constants.CHG_LOG_SOURCE_VERSION, rq_obj.sourceVersion, ref statusMayHaveChanged);if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SOURCE_SECTION, Constants.CHG_LOG_SOURCE_SECTION, rq_obj.sourceSection, ref statusMayHaveChanged);if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_SUBSYSTEM, Constants.CHG_LOG_SUBSYSTEM_TYPE, rq_obj.subsystem, ref statusMayHaveChanged);if_tagged_value_changed_update_it(gChangeLog, ea_req, Constants.TAG_STABILITY, Constants.CHG_LOG_STABILITY, rq_obj.stability, ref statusMayHaveChanged);// Requirement Types are actually implemented using ordinary EA element stereotypes.string[] sar = ea_req.StereotypeEx.Split(",".ToCharArray());// if we didnt get a requirement type from the ReqPro database, set it to an empty string so that it can// be compared to the EA stereotype strings.if (rq_obj.type == null)rq_obj.type = "";if (sar.Length > 0){// The first item in the stereotype list is the one on display in the EA element properties and so is the one the user// thinks is assigned to the requirement element, so check against that for changesstring sar0 = sar[0].Trim();if (!sar0.Equals(rq_obj.type)){gChangeLog.Notes += ea_req.ElementGUID + ":" + Constants.CHG_LOG_REQ_TYPE + ":" + sar0 + ":" + rq_obj.type + "\r\n";gChangeLog.Update();statusMayHaveChanged = true;ea_req.StereotypeEx = rq_obj.type;ea_req.Stereotype = rq_obj.type;}}else if (!ea_req.Stereotype.Equals(rq_obj.type)){gChangeLog.Notes += ea_req.ElementGUID + ":" + Constants.CHG_LOG_REQ_TYPE + ":" + ea_req.Stereotype + ":" + rq_obj.type + "\r\n";gChangeLog.Update();statusMayHaveChanged = true;ea_req.StereotypeEx = rq_obj.type;ea_req.Stereotype = rq_obj.type;}ea_req.Update();if (meaningMayHaveChanged){gModifiedRequirementCount++;}if (statusMayHaveChanged){gStatusUpdatedRequirementCount++;}totalRequirements++;}}else{// update orphanage detection datarq_obj.matched = true;totalRequirements++;// This ReqPro requirement does not have a counterpart in EA, so we must create// a new one.gNewRequirementCount++;// create the new EA requirement in the import packageEA.Element ea_req = (EA.Element)gChangeHistoryPackage.Elements.AddNew( rq_obj.tag + " " + rq_obj.name, "Requirement");rq_obj.ea_element_ID = ea_req.ElementID;CopyReqProDatabase.copyReq(ea_req, rq_obj);ea_req.Notes = rq_obj.text;ea_req.Update();// add the new requirement to our list for later processinggReq_lists.new_ea_reqs.Add(ea_req);gReq_lists.new_ea_GUIDs.Add(rq_obj.guid);gChangeLog.Notes += ea_req.ElementGUID + ":Created:" + ea_req.Name + "\r\n";gChangeLog.Update();Main.WriteOutput("Created: " + ea_req.Name, ea_req.ElementID);}}catch (Exception ex){Main.WriteOutput("Exception (ImportFromReqPro), " + ex.Message + ", " + rq_obj.name, -1);}}if (Main.mustAbort)return;Main.WriteOutput("Checking for orphaned requirements", -1);// Now do initial check for orphaned EA requirementsfor (int i = 0; i < gReq_lists.ea_GUIDs.Count; i++){if (Main.mustAbort)break;string rq_GUID = (string)gReq_lists.ea_GUIDs[i];if (rq_GUID != ""){if ((bool)ea_req_matched[i] == false){EA.Element ea_req = (EA.Element)gReq_lists.ea_reqs[i];add_to_orphan_list(ref ea_req);}}}// Now we import the ReqPro database package structure and migrate all of our existing or// new requirements into it. We only create the structure we need, ie. empty package structure// will not be created unecessarily.Main.WriteOutput("Importing latest ReqPro database package structure", -1);populate_latest_reqprodb_structure_package(rq_root_package);gNewReqProDBPackage.Packages.Refresh();gNewReqProDBPackage.Elements.Refresh();gNewReqProDBPackage.Diagrams.Refresh();Main.EA_Repository.RefreshModelView(parentPackage.PackageID);if (Main.mustAbort)return;// If there is any change to report, ask user if they want to see it now, and if so, display itif (gChangeLog.Notes != null && gChangeLog.Notes.Length > 0){dlgres = MessageBoxEx.Show("Display Change Log?", "Change Log", MessageBoxButtons.YesNo);if (dlgres == DialogResult.Yes)displayChangeLog(gChangeLog);}if (Main.mustAbort)return;// Take care of orphaned packages that have been orphaned due to a reduction in the scope of import.//Main.WriteOutput("Re-locating orphaned requirements (if any)", -1);//foreach (EA.Element ea_req_orphaned in ea_reqs_orphaned)//{// ea_req_orphaned.PackageID = gOrphansPackage.PackageID;// ea_req_orphaned.Update();//}// Remove the old superseded facsimile of the ReqPro database package structure. At this point,// orphaned requirements left in these will be moved to the Orphaned package, before the old// structure package is removed.Main.WriteOutput("Removing old ReqPro database package structures", -1);remove_old_empty_structures(parentPackage, gNewReqProDBPackage);if (Main.mustAbort)return;// Remove other empty packages - this tidies up the ReqPro import area so that at the top level// all a user should see is the Change History package, ReqProDB package, and possibly an Orphans package.Main.WriteOutput("Pruning empty packages", -1);prune_empty_packages(parentPackage);// Refresh the project browserMain.EA_Repository.RefreshModelView(parentPackage.PackageID);// Setup the internal requirement to requirement connectivity. This is seperate from the browser// organisation of elements in EA, but since in ReqPro, that organisation tells part of the story// of requirement to requirement connectivity, it is mirrored in the internal connectivity, along// with explicit trace relationships setup in ReqPro.// The purpose of this internal connectivity is to support relationship matrix tables in documentation// generated by EA_DocGen, or to support diagrammatic representations of the requirements in EA,// where the connectivity will become apparent through links ajoining the requirement elements in the// diagram.write_traces(totalRequirements);if (Main.mustAbort)return;// Output any messages the underlying parser may have collected as a result of observations it has made// regarding the data it has read from the ReqPro database.writeDelayedMessages();// display summary statsMain.WriteOutput(gNewRequirementCount.ToString() + " new requirements", -1);Main.WriteOutput(gModifiedRequirementCount.ToString() + " requirements modified", -1);Main.WriteOutput(gStatusUpdatedRequirementCount.ToString() + " requirements had status changes", -1);Main.WriteOutput(gOrphanedCount.ToString() + " requirements were orphaned", -1);Main.WriteOutput("Import Completed", -1);MessageBoxEx.Show("Import Completed");}catch (Exception ex){Main.MessageBoxException(ex, "Exception (ImportFromReqPro)");}}private string updated_property_value_if_changed(EA.Element changeLog,EA.Element ea_req,string dest_value,string changeLogName,string source_value,ref bool mayHaveChanged){if (dest_value.CompareTo(source_value) != 0 ){changeLog.Notes += ea_req.ElementGUID + ":" + changeLogName+ ":" + NO_COLONS(dest_value) + ":" + NO_COLONS(source_value) + "\r\n";changeLog.Update();mayHaveChanged = true;return source_value;}return dest_value;}private void if_tagged_value_changed_update_it(EA.Element changeLog,EA.Element ea_req,string tagname,string changeLogName,string source_value,ref bool statusMayHaveChanged){string value = EA_TaggedValues.Read(ea_req, tagname, "");if ((source_value != null) && (false == source_value.Equals(value))){changeLog.Notes += ea_req.ElementGUID + ":" + changeLogName + ":" + value + ":" + source_value + "\r\n";changeLog.Update();EA_TaggedValues.Write(ea_req, tagname, source_value);statusMayHaveChanged = true;}}/// <summary>/// This method (along with its sibling overload) obtains a flattened view of the/// hierarchical requirements obtained from the ReqPro database. This accumulation/// takes account of the users filtering requests./// </summary>/// <param name="ea_repository"></param>private void get_rq_req_collection(ref ArrayList rq_req_collection){foreach( ReqPro_object sub_obj in rq_root_package.ReqPro_objects ){get_rq_req_collection(ref rq_req_collection, sub_obj);}}private void get_rq_req_collection(ref ArrayList rq_req_collection,ReqPro_object rq_obj ){if (rq_obj.isPackage){// if the package is not filtered, or if we are allowed to dig beneath filtered// packages...if (rq_obj.filtered == false || base.allowPackageStructureFragments){// Using recursion, scan this objects sub-objectsforeach( ReqPro_object sub_obj in rq_obj.ReqPro_objects ){// if the sub-object is a package, always recurse to process it, else// if the sub-object is a requirement, only recurse if the containing package// is not filtered, so that requirements of filtered packages are themselves// filtered out by virtue of their parent packages filtering state.if ( sub_obj.isPackage|| (sub_obj.isRequirement && rq_obj.filtered == false)){get_rq_req_collection(ref rq_req_collection, sub_obj);}}}}else if (rq_obj.isRequirement){if ( ! (reqTypeIsFiltered(rq_obj) || reqStatusTypeIsFiltered(rq_obj)) ){// Add this requirement to the flattened list we are accumulatingrq_req_collection.Add(rq_obj);// In ReqPro, a requirement can be related to another requirement in several ways:// 1. By virtue of its position relative to another in the browser display. That is,// if you place a requirement beneath a parent requirement, you are establishing a// parent-child relationship.// 2. By virtue of a specific TraceTo relationship being made via the Traceability menu// option.// Interestingly, ReqPro prevents you creating a relationship of one of the above types,// if a relationship of the other type already exists. This implies that the relationship// between a parent and child requirement must be singular and is important regardless// of the manner in which it was created or represented.// The CopyReqProDatabaseToMemory base class will already have taken care of recording// relationships detected from ReqPro made using method 2 above. What we need to do here is// take care of those apparent from the browser based hierarchical organisation (ie. made// in ReqPro using method 1).// All we need to do is grab the parent object (if any) and add the GUID of the child to// its list of trace-to's. Later on, the write_traces() class method will turn these into// EA based connection objects that belong to the parent requirement elements.if (rq_obj.parent != null){rq_obj.parent.ReqPro_traces.Add(rq_obj.guid);}// Using recursion, scan this objects sub-objects, which in practise will all// be requirement objects. At this time requirement objects cannot have packages// as children.foreach( ReqPro_object sub_obj in rq_obj.ReqPro_objects ){get_rq_req_collection(ref rq_req_collection, sub_obj);}}}}/// <summary>/// Populates a ReqProDB structure package to be used as a home for the requirements within the/// overall import container, and re-locate requirements into it from wherever they may be elsewhere/// within and under the import container./// </summary>/// <param name="rq_obj"></param>private void populate_latest_reqprodb_structure_package(ReqPro_object rq_obj){foreach( ReqPro_object sub_obj in rq_obj.ReqPro_objects ){if (Main.mustAbort)return;if (sub_obj.isPackage){populate_latest_reqprodb_structure_package(sub_obj); // recurse}else if (sub_obj.isRequirement){EA.Element ea_req = null;// Examine all of our requirement lists to try and re-locate the requirements from// the old structure(s) to the new one.// re-locate existing requirements updated during this importint i_ea_req;i_ea_req = gReq_lists.ea_GUIDs.IndexOf( sub_obj.guid );if (0 <= i_ea_req){ea_req = ((EA.Element)gReq_lists.ea_reqs[i_ea_req]);EA.Package pkg = get_or_create_latest_structure(sub_obj);if (sub_obj.matched == false){add_to_orphan_list(ref ea_req);ea_req.PackageID = gOrphansPackage.PackageID; // re-locateea_req.Update();}else if (sub_obj.filtered == false){ea_req.PackageID = pkg.PackageID; // re-locateea_req.TreePos = sub_obj.treePos;ea_req.Update();}else{// QUESTION: So what if it is filtered? What happens to it then?// Will it stay in the same place or be relocated as an orphan?// ANSWER: relocate_elements_to_orphans_package() does this, called later// in the import sequence.}}else{// re-locate new requirements that have come in for the first time on this import.i_ea_req = gReq_lists.new_ea_GUIDs.IndexOf( sub_obj.guid );if (0 <= i_ea_req){ea_req = ((EA.Element)gReq_lists.new_ea_reqs[i_ea_req]);EA.Package pkg = get_or_create_latest_structure(sub_obj);ea_req.PackageID = pkg.PackageID; // re-locateea_req.TreePos = sub_obj.treePos;ea_req.Update();}}// Re-locate requirement under its parent requirement, if such a relationship exists.if (ea_req != null && sub_obj.parent.isRequirement){// Only do this if the parent requirement has not been orphaned or filtered out of the importif (sub_obj.parent.ea_element_ID != -1){EA.Element parent_ea_req = Main.EA_Repository.GetElementByID(sub_obj.parent.ea_element_ID);if (parent_ea_req != null){// make sure we only assign ele-to-ele relationship if both elements are in the same// packageif (ea_req.PackageID == parent_ea_req.PackageID){ea_req.ParentID = parent_ea_req.ElementID; // re-locateea_req.TreePos = sub_obj.treePos;ea_req.Update();}}}else{// TODO: what if the parent requirement has been orphaned, but the child as represented by// the current value of sub_obj has not been? Will that have already been taken care of by// the code earlier in this function that re-locates existing requirements to the correct// parent package?}}populate_latest_reqprodb_structure_package(sub_obj); // recurse}}}/// <summary>/// This is a helper for the populate_latest_reqprodb_structure_package() function. It allows on-the-fly/// EA package path construction, triggered whenever we find we have a requirement to/// re-locate into the latest facsimile of the ReqPro database structure./// </summary>/// <param name="rq_obj"></param>/// <returns></returns>private EA.Package get_or_create_latest_structure(ReqPro_object rq_obj){// go up the tree to find the immediate enclosing package of the requirement represented// by the input parameter. We do this because it is certain that this function will be called// initially passing a ReqPro_object that has "isRequirement" set to true. The requirement object// may also be nested within another requirement object, as a result of its parent-child hierarchy// in ReqPro.while(rq_obj.isRequirement && rq_obj.parent != null){rq_obj = rq_obj.parent;}// Now that we have reached a package, we can figure out if we need to create its facsimile in EA// or detect if that has already been done on a previous invocation.if (rq_obj.isPackage){// if this package has already been related to an EA package, then simply return it. The EA package// must have been created ona previous invocation of this function.if (rq_obj.ea_element_ID != -1){return Main.EA_Repository.GetPackageByID(rq_obj.ea_element_ID);}else // need to create this package, and if necessary, its parental line right up to the root package{// use recursion to work our way up the package ancestral line until we hit a point where// we find an EA package, then unwind, creating the sub-packages down to where our requirement// is to be held.EA.Package pkg = get_or_create_latest_structure(rq_obj.parent); // recurseif (pkg != null){if (rq_obj.filtered){return pkg;}else{EA.Package sub_pkg = EA_Package.create(pkg, rq_obj.name, rq_obj.treePos);// attach the EA package to the ReqPro_object so that we do not attempt to create this// package again on another call into this function.rq_obj.ea_element_ID = sub_pkg.PackageID;return sub_pkg;}}}}// default to returning the root level package in EA where the requirement structure begins.// This will always be valid and available, but hopefully, the execution path wont drop down// to here.return Main.EA_Repository.GetPackageByID(rq_root_package.ea_element_ID);}/// <summary>/// Remove the old ReqProDB structure packages if they are now empty of requirements. IF some/// requirements remain in them, they are moved to the orphaned package so that the old ReqProDB/// packages can be removed./// </summary>/// <param name="parentPackage"></param>/// <param name="new_structure_pkg"></param>private void remove_old_empty_structures(EA.Package parentPackage, EA.Package new_structure_pkg){// NOTE: The parentPackage is the container of the ReqProDB elementshort i;for(i=0; i<parentPackage.Packages.Count; i++){gCurrentPackage = (EA.Package)parentPackage.Packages.GetAt(i);// dont do anything to the new structure package that has been created on the current import cycle.if (new_structure_pkg != null && gCurrentPackage.PackageID == new_structure_pkg.PackageID){continue;}// skip any packages that are not ReqProDB structure packagesif (!gCurrentPackage.Name.StartsWith("ReqProDB")){continue;}// re-locate all elements to the orphans packagerelocate_elements_to_orphans_package(gCurrentPackage);// Now delete the old ReqProDB structure packageparentPackage.Packages.DeleteAt(i, false);}parentPackage.Packages.Refresh();Main.EA_Repository.RefreshModelView(parentPackage.PackageID);}/// <summary>/// Re-locate all requirements that remain under a ReqProDB structure package, to its/// top level, so that later on, the package hierarchy within it can be removed./// </summary>/// <param name="parentPackage"></param>private void relocate_elements_to_orphans_package(EA.Package parentPackage){if (parentPackage.PackageID != gOrphansPackage.PackageID){// relocate elements and diagrams to the top level of the orphans packageforeach(EA.Element ele in parentPackage.Elements){ele.PackageID = gOrphansPackage.PackageID;ele.Update();}foreach(EA.Diagram diag in parentPackage.Diagrams){diag.PackageID = gOrphansPackage.PackageID;diag.Update();}// now parse lower level packages in the treeforeach(EA.Package sub_pkg in parentPackage.Packages){relocate_elements_to_orphans_package(sub_pkg); // recurse}}}/// <summary>/// This function migrates the older form of import package and its change log file into the newer/// Change History package, and deletes the old import package. In doing this, it will move any/// requirements still remaining in the import package to the top level of the new ReqProDB structure/// package./// </summary>/// <param name="parentPackage"></param>private void migrate_old_import_packages_to_change_history_package(EA.Package parentPackage){if (gNewReqProDBPackage != null && gChangeHistoryPackage != null){bool found = false;short i;for(i=0; i<parentPackage.Packages.Count; i++){EA.Package sub_pkg = (EA.Package)parentPackage.Packages.GetAt(i);if (sub_pkg.Name.StartsWith("import")){if (!found){found = true;Main.WriteOutput("Migrating old import package contents to Change History and ReqProDB packages", -1);}// re-locate all elements from the old import package to the new ReqProDB structure package.migrate_old_import_packages_to_change_history_package_recurser(sub_pkg);// delete the old import packageparentPackage.Packages.DeleteAt(i, false);}}parentPackage.Packages.Refresh();Main.EA_Repository.RefreshModelView(parentPackage.PackageID);}}/// <summary>/// Moves all elements out of an old-style import package, and into the new ReqProDB structure package./// Change Log elements are handled in a special way such that they are renamed from plain "Change Log"/// to the new style "Change Log - import <date> <time>" format, and are moved into the new "Change History"/// package./// </summary>/// <param name="parentPackage"></param>private void migrate_old_import_packages_to_change_history_package_recurser(EA.Package parentPackage){foreach(EA.Element ele in parentPackage.Elements){if (ele.Name.StartsWith("Change Log")){ele.Name = ele.Name + " - " + parentPackage.Name;ele.PackageID = gChangeHistoryPackage.PackageID;ele.Update();gChangeHistoryPackage.Elements.Refresh();}else{ele.PackageID = gNewReqProDBPackage.PackageID;ele.Update();}}foreach(EA.Diagram diag in parentPackage.Diagrams){diag.PackageID = gNewReqProDBPackage.PackageID;diag.Update();}foreach(EA.Package sub_pkg in parentPackage.Packages){migrate_old_import_packages_to_change_history_package_recurser(sub_pkg); // recurse}}private EA.Package find_or_create_change_history_package(EA.Package parentPackage){foreach(EA.Package sub_pkg in parentPackage.Packages){if (sub_pkg.Name.StartsWith("Change History")){return sub_pkg;}}EA.Package pkg = EA_Package.create(parentPackage, "Change History", 1);return pkg;}private EA.Package find_or_create_orphans_package(EA.Package parentPackage){foreach(EA.Package sub_pkg in parentPackage.Packages){if (sub_pkg.Name.StartsWith("Orphaned Requirements")){return sub_pkg;}}EA.Package pkg = EA_Package.create(parentPackage, "Orphaned Requirements", 1);return pkg;}/// <summary>/// This function removes any empty packages it can find in the ReqPro import area. This is/// a tidying up operation./// </summary>/// <param name="parentPackage"></param>/// <returns></returns>private bool prune_empty_packages(EA.Package parentPackage){bool retVal = false;bool needRefresh = false;short i;for(i=0; i<parentPackage.Packages.Count; i++){EA.Package sub_pkg = (EA.Package)parentPackage.Packages.GetAt(i);if (true == prune_empty_packages(sub_pkg)) // recurse{parentPackage.Packages.DeleteAt(i, false);needRefresh = true;}}if (needRefresh){parentPackage.Packages.Refresh();}if (parentPackage.Packages.Count == 0&& parentPackage.Elements.Count == 0&& parentPackage.Diagrams.Count == 0){retVal = true;}return retVal;}private void add_to_orphan_list(ref EA.Element ea_req){if (!gReq_lists.orphaned_ea_reqs.Contains(ea_req.ElementID)){gReq_lists.orphaned_ea_reqs.Add(ea_req.ElementID);gOrphanedCount++;gChangeLog.Notes += ea_req.ElementGUID + ":Orphaned:" + ea_req.Name + "\r\n";gChangeLog.Update();}}}}