Rev 2132 | Blame | Compare with Previous | Last modification | View Log | RSS feed
using System;using System.Collections;using System.Text;using System.ComponentModel;using System.Windows.Forms;using Microsoft.Office.Interop.Word;namespace EA_DocGen{/// <summary>/// Summary description for EA_RelationshipMatrix./// </summary>public class EA_RelationshipMatrix{public struct RM_ColumnType{public string columnTitle;public string packageGUID;public string packageName;public ArrayList elementTypes;public bool packageRecursion;public bool packageTrimming;public bool elementNotes;public bool reqTagSort;};public struct RM_TableType{public string introText;public string title;};public struct RM_MatrixType{public int style;public RM_TableType fromToTable;public RM_TableType toFromTable;public RM_ColumnType from;public RM_ColumnType to;};// DATAprivate static RM_MatrixType RM_Matrix;private static EA.Element RM_Element = null;private static EA.Package fromPackage = null;private static EA.Package toPackage = null;private static ArrayList processedElements = null;private static string [] options_string = null;private enum DictionaryValueType{DVT_EA_ELEMENT,DVT_STRING};public static void initialise(ArrayList processedElementsRef){processedElements = processedElementsRef;RM_Matrix.from.elementTypes = new ArrayList();RM_Matrix.to.elementTypes = new ArrayList();}private static void reset(){RM_Matrix.style = 1;RM_Matrix.fromToTable.introText = null;RM_Matrix.fromToTable.title = null;RM_Matrix.from.columnTitle = null;RM_Matrix.from.packageGUID = null;RM_Matrix.from.elementTypes.Clear();RM_Matrix.from.packageRecursion = true;RM_Matrix.from.packageTrimming = false;RM_Matrix.from.elementNotes = false;RM_Matrix.from.packageName = null;RM_Matrix.from.reqTagSort = true;fromPackage = null;RM_Matrix.toFromTable.introText = null;RM_Matrix.toFromTable.title = null;RM_Matrix.to.columnTitle = null;RM_Matrix.to.packageGUID = null;RM_Matrix.to.elementTypes.Clear();RM_Matrix.to.packageRecursion = true;RM_Matrix.to.packageTrimming = false;RM_Matrix.to.elementNotes = false;RM_Matrix.to.packageName = null;RM_Matrix.to.reqTagSort = true;toPackage = null;}public static bool processRelationshipMatrixOptions(EA.Element theElement){RM_Element = theElement;reset();// Extract the control options from the notes of the elementstring delimStr = "\n";char [] delim = delimStr.ToCharArray();options_string = theElement.Notes.Split(delim,100);int i = 0;foreach(string s in options_string){options_string[i] = s.Trim();i++;}// scan the options string and assign values into the options variables as requiredforeach(string s in options_string){if (s.Length > 0 && s != "\n" && s != "\r" && s[0] != '\\'){if (s.StartsWith("style=")){RM_Matrix.style = EA_DocGenOptions.getOptionValue(s, RM_Matrix.style);}// FROM itemselse if (s.StartsWith("fromIntroText=")){RM_Matrix.fromToTable.introText = EA_DocGenOptions.getOptionValue(s, null);}else if (s.StartsWith("fromToTableTitle=")){RM_Matrix.fromToTable.title = EA_DocGenOptions.getOptionValue(s, null);}else if (s.StartsWith("fromColumnTitle=")){RM_Matrix.from.columnTitle = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.columnTitle);}else if (s.StartsWith("fromPackageRecursion=")){RM_Matrix.from.packageRecursion = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.packageRecursion);}else if (s.StartsWith("fromPackageTrimming=")){RM_Matrix.from.packageTrimming = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.packageTrimming);}else if (s.StartsWith("fromPackage=")){RM_Matrix.from.packageGUID = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.packageGUID);}else if (s.StartsWith("fromElementNotes=")){RM_Matrix.from.elementNotes = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.elementNotes);}else if (s.StartsWith("fromPackageName=")){RM_Matrix.from.packageName = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.packageName);}else if (s.StartsWith("fromElementType=")){string et = EA_DocGenOptions.getOptionValue(s,null);if (et != null){RM_Matrix.from.elementTypes.Add( et );}}else if (s.StartsWith("fromUsesTagSort=")){RM_Matrix.from.reqTagSort = EA_DocGenOptions.getOptionValue(s, RM_Matrix.from.reqTagSort);}// TO itemselse if (s.StartsWith("toIntroText=")){RM_Matrix.toFromTable.introText = EA_DocGenOptions.getOptionValue(s, null);}else if (s.StartsWith("toFromTableTitle=")){RM_Matrix.toFromTable.title = EA_DocGenOptions.getOptionValue(s, null);}else if (s.StartsWith("toColumnTitle=")){RM_Matrix.to.columnTitle = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.columnTitle);}else if (s.StartsWith("toPackageRecursion=")){RM_Matrix.to.packageRecursion = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.packageRecursion);}else if (s.StartsWith("toPackageTrimming=")){RM_Matrix.to.packageTrimming = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.packageTrimming);}else if (s.StartsWith("toPackage=")){RM_Matrix.to.packageGUID = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.packageGUID);}else if (s.StartsWith("toElementNotes=")){RM_Matrix.to.elementNotes = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.elementNotes);}else if (s.StartsWith("toPackageName=")){RM_Matrix.to.packageName = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.packageName);}else if (s.StartsWith("toElementType=")){string et = EA_DocGenOptions.getOptionValue(s,null);if (et != null){RM_Matrix.to.elementTypes.Add( et );}}else if (s.StartsWith("toUsesTagSort=")){RM_Matrix.to.reqTagSort = EA_DocGenOptions.getOptionValue(s, RM_Matrix.to.reqTagSort);}}}// Verify that we have all the necessary compulsory optionsif (RM_Matrix.fromToTable.title == null && RM_Matrix.toFromTable.title == null){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option: Must specify at least one table title");return false;}// FROM itemsif (RM_Matrix.from.columnTitle == null){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option: fromColumnTitle");return false;}if (RM_Matrix.from.packageGUID == null){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option: fromPackageGUID");return false;}if (RM_Matrix.from.elementTypes.Count == 0){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option(s): fromElementTypes");return false;}// TO itemsif (RM_Matrix.to.columnTitle == null){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option: toColumnTitle");return false;}if (RM_Matrix.to.packageGUID == null){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option: toPackageGUID");return false;}if (RM_Matrix.to.elementTypes.Count == 0){MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +"Missing option(s): toElementTypes");return false;}// Find GUID linked packages in the repositoryfromPackage = Main.EA_Repository.GetPackageByGuid( RM_Matrix.from.packageGUID );if (fromPackage == null){MessageBox.Show("Error processing EA_DocGenRelationshipMatrix\n\n" +"could not locate fromPackage in Repository");return false;}toPackage = Main.EA_Repository.GetPackageByGuid( RM_Matrix.to.packageGUID );if (toPackage == null){MessageBox.Show("Error processing EA_DocGenRelationshipMatrix\n\n" +"could not locate toPackage in Repository");return false;}return true;}public static string optionTemplateForRelationshipMatrix(int style){switch (style){case 1:return"style=1\r\n"+ "fromToTableTitle=\r\n"+ "fromIntroText=\r\n"+ "fromColumnTitle=\r\n"+ "fromPackage=\r\n"+ "fromElementType=\r\n"+ "fromElementType=\r\n"+ "fromPackageRecursion=true\r\n"+ "fromPackageTrimming=false\r\n"+ "fromElementNotes=false\r\n"+ "fromUsesTagSort=true\r\n"+ "toFromTableTitle=\r\n"+ "toIntroText=\r\n"+ "toColumnTitle=\r\n"+ "toPackage=\r\n"+ "toElementType=\r\n"+ "toPackageRecursion=true\r\n"+ "toPackageTrimming=false\r\n"+ "toElementNotes=false\r\n"+ "toUsesTagSort=true\r\n";case 2:return"style=2\r\n"+ "fromToTableTitle=Requirement Dependencies\r\n"+ "fromIntroText=The following table identifies the source requirements (if any) for each requirement stated in this document.\r\n"+ "fromColumnTitle=Requirement\r\n"+ "fromPackage=\r\n"+ "fromElementType=Requirement\r\n"+ "fromPackageRecursion=true\r\n"+ "fromPackageTrimming=false\r\n"+ "fromElementNotes=false\r\n"+ "fromUsesTagSort=true\r\n"+ "toPackageRecursion=true\r\n"+ "toPackage=\r\n"+ "toColumnTitle=Source Requirements\r\n"+ "toElementType=Requirement\r\n";default:return "";}}/// <summary>/// This function will insert one or more relationship matrix tables into the document,/// built from content specified in options that are provided in the notes section of/// the special element that has led to this function being called./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>public static void processRelationshipMatrixElement( EA.Element theElement, int recurse_level ){RM_Element = theElement;if (RM_Matrix.from.packageName != null && fromPackage != null){EA.Package newFromPackage = EA_Utilities.findNamedPackage(fromPackage, RM_Matrix.from.packageName);if (newFromPackage != null){fromPackage = newFromPackage;}else{MessageBox.Show("Error processing EA_DocGenRelationshipMatrix\n\n" +"could not locate fromPackageName in Repository");return;}}if (RM_Matrix.to.packageName != null && toPackage != null){EA.Package newToPackage = EA_Utilities.findNamedPackage(toPackage, RM_Matrix.to.packageName);if (newToPackage != null){toPackage = newToPackage;}else{MessageBox.Show("Error processing EA_DocGenRelationshipMatrix\n\n" +"could not locate toPackageName in Repository");return;}}switch (RM_Matrix.style){case 1:processRelationshipMatrixElementStyle1();break;case 2:processRelationshipMatrixElementStyle2();break;}}/// <summary>/// This function will insert a relationship matrix table into the document, built from content/// specified in options that are provided in the notes section of the special element that/// has led to this function being called.////// The style of these tables is that required for requirement to design traceability./// </summary>/// <param name="EA_RelMatrix"></param>private static void processRelationshipMatrixElementStyle1(){int tableNum = 0;Table table = null;// Scan the fromPackage to find all the "from elements".Main.WriteOutput(" Finding elements in fromPackage", -1);ElementAccumulator fromLister = new ElementAccumulator(RM_Matrix.from.elementTypes);EA_Utilities.findAndProcessPackageElements( fromPackage, fromLister, RM_Matrix.from.packageRecursion );if (createWordDoc.abortCreationThread)return;// Scan the toPackage to find all the "to elements".Main.WriteOutput(" Finding elements in toPackage", -1);ElementAccumulator toLister = new ElementAccumulator(RM_Matrix.to.elementTypes);EA_Utilities.findAndProcessPackageElements( toPackage, toLister, RM_Matrix.to.packageRecursion );if (createWordDoc.abortCreationThread)return;// Sort the "from elements"Main.WriteOutput(" Sorting elements in fromPackage", -1);if (RM_Matrix.from.reqTagSort){elementSortByTAG sorter = new elementSortByTAG();fromLister.Elements.Sort( sorter );}else{elementSortByName sorter = new elementSortByName();fromLister.Elements.Sort( sorter );}// Sort the "to" elementsMain.WriteOutput(" Sorting elements in toPackage", -1);if (RM_Matrix.to.reqTagSort){elementSortByTAG sorter = new elementSortByTAG();toLister.Elements.Sort( sorter );}else{elementSortByName sorter = new elementSortByName();toLister.Elements.Sort( sorter );}if (createWordDoc.abortCreationThread)return;// dictionary to support from-to table construction.ArrayList fromToDictionary = new ArrayList();// dictionary to support to-from table construction.ArrayList toFromDictionary = new ArrayList();bool needFromToTable = false;if (RM_Matrix.fromToTable.title != null && RM_Matrix.fromToTable.title.Length > 0)needFromToTable = true;bool needToFromTable = false;if (RM_Matrix.toFromTable.title != null && RM_Matrix.toFromTable.title.Length > 0)needToFromTable = true;// NOTE: this code has to execute even if no from-to table is needed, in order to support the// generation of a to-from table, assuming the user has requested one.int numberOfFromToRows = 0;Main.WriteOutput(" Creating from-to Dictionary", -1);foreach(EA.Element fromElement in fromLister.Elements){if (createWordDoc.abortCreationThread)return;// look at the elements connection collection to find references to the// destination elementsbool foundToElement = false;EA.Collection conCollection = fromElement.Connectors;foreach (EA.Connector thisCon in conCollection){if (createWordDoc.abortCreationThread)return;EA.Element destE = Main.EA_Repository.GetElementByID( thisCon.SupplierID );if (destE == null)continue;// ignore self-referential connectionsif (fromElement.ElementID == thisCon.SupplierID)continue;// if the destination element is of a type that the user has requested to include...if (!RM_Matrix.to.elementTypes.Contains( destE.Type ))continue;// if the destination element is in the "to list"...if ( !toLister.ElementIds.Contains(destE.ElementID) )continue;// Capture the from-to relationship in a dictionary where the key is the// "from element", and the value is the "to element".DictionaryEntry newFromToEntry = new DictionaryEntry(fromElement, destE);fromToDictionary.Add( newFromToEntry );foundToElement = true;// Capture the from-to relationship in a dictionary where the key is the// ID of the destination element, and the value is the from element name.// This dictionary will enable rapid construction of the to-from table if// the user has requested it, from the exact same relationship info used// to construct the from-to table.if (needToFromTable){DictionaryEntry newToFromEntry = new DictionaryEntry(destE.ElementID, fromElement.Name);toFromDictionary.Add( newToFromEntry );}}// If we found a from-to relationship that table needs a new row.if (foundToElement){numberOfFromToRows++;}// If we did not find a from-to relationship that table still needs a new row// if the user wants all "from elements", even if some have no "to elements".else if (RM_Matrix.from.packageTrimming == false){DictionaryEntry newFromToEntry = new DictionaryEntry(fromElement, null);fromToDictionary.Add( newFromToEntry );numberOfFromToRows++;}}if (createWordDoc.abortCreationThread)return;if (needFromToTable){// Now we can actually serialise the tableMain.WriteOutput(" Creating from-to table", -1);if (RM_Matrix.fromToTable.introText != null && RM_Matrix.fromToTable.introText.Length > 0){Range wr = TextualContent.appendAndSelectText( RM_Matrix.fromToTable.introText, EA_Constants.styleName_Body1 );object obj = string.Format("EA_DocGen Informational Comment - This table has been produced using the following options:\n\n{0}", RM_Element.Notes);createWordDoc.WordDocument.Comments.Add(wr, ref obj);}// create the from-to table in the word doctableNum = TabularContent.Table_Create( RM_Matrix.fromToTable.title, true, numberOfFromToRows + 1, 2 );table = createWordDoc.WordDocument.Tables[tableNum];TabularContent.Table_SetTableColumnTitle(table, RM_Matrix.from.columnTitle, 1);TabularContent.Table_SetTableColumnTitle(table, RM_Matrix.to.columnTitle, 2);fillInTable(fromToDictionary, table, DictionaryValueType.DVT_EA_ELEMENT);}if (createWordDoc.abortCreationThread)return;// Does user want a to-from table ?if (needToFromTable){Main.WriteOutput(" Creating to-from Dictionary", -1);// update fromElementNotes with toElementNotes settingRM_Matrix.from.elementNotes = RM_Matrix.to.elementNotes;// re-use the fromToDictionary to prepare the to-from table contentfromToDictionary.Clear();// We've already found all elements for the left hand column of the to-from table.// They are stored in the toListerif (createWordDoc.abortCreationThread)return;// To make the to-from table, we use the dictionary that was built when making the from-to// table. The dictionary will allow rapid determination of what "from" items belong to each// "to" item, without us having to go back to EA and enquire on the database.// We build a new fromToDictionary from the toFromDictionary, in advance of actually making// the table so that we can figure out how many rows the table needs to have.numberOfFromToRows = 0;foreach(EA.Element toElement in toLister.Elements){if (createWordDoc.abortCreationThread)return;bool foundToElement = false;// right-column cell contentforeach (DictionaryEntry de in toFromDictionary){if ((int)de.Key == toElement.ElementID){DictionaryEntry newFromToEntry = new DictionaryEntry(toElement, (string)de.Value);fromToDictionary.Add( newFromToEntry );foundToElement = true;}}if (createWordDoc.abortCreationThread)return;// If we found a from-to relationship that table needs a new row.if (foundToElement){numberOfFromToRows++;}// if user wants all "from elements", even if some have no "to elements", then add a dictionary// entry and bump row count.else if (RM_Matrix.to.packageTrimming == false){DictionaryEntry newFromToEntry = new DictionaryEntry(toElement, "");fromToDictionary.Add( newFromToEntry );numberOfFromToRows++;}}if (createWordDoc.abortCreationThread)return;// Now begin to add the to-from table to the word documentMain.WriteOutput(" Creating to-from table", -1);if (RM_Matrix.toFromTable.introText != null && RM_Matrix.toFromTable.introText.Length > 0){Range wr = TextualContent.appendAndSelectText( RM_Matrix.toFromTable.introText, EA_Constants.styleName_Body1 );// add options comment only if user did not generate a from-to table, since there is no point in repeating// the options comment which applies to both from-to and to-from table pair.if (!needFromToTable){object obj = string.Format("EA_DocGen Informational Comment - This table has been produced using the following options:\n\n{0}", RM_Element.Notes);createWordDoc.WordDocument.Comments.Add(wr, ref obj);}}// create the table in the word doctableNum = TabularContent.Table_Create( RM_Matrix.toFromTable.title, true, numberOfFromToRows + 1, 2 );table = createWordDoc.WordDocument.Tables[tableNum];TabularContent.Table_SetTableColumnTitle(table, RM_Matrix.to.columnTitle, 1);TabularContent.Table_SetTableColumnTitle(table, RM_Matrix.from.columnTitle, 2);fillInTable(fromToDictionary, table, DictionaryValueType.DVT_STRING);}}private static void fillInTable(ArrayList fromToDictionary, Table table, DictionaryValueType valueType){int lastFromElementId = -1;bool firstCellContent = true;int row = 1;foreach (DictionaryEntry de in fromToDictionary){if (createWordDoc.abortCreationThread)return;if ( ((EA.Element)de.Key).ElementID != lastFromElementId ){lastFromElementId = ((EA.Element)de.Key).ElementID;row++;table.Cell(row,1).Range.Text = ((EA.Element)de.Key).Name;firstCellContent = true;if (RM_Matrix.from.elementNotes){if (((EA.Element)de.Key).Notes != null && ((EA.Element)de.Key).Notes.Length > 0){table.Cell(row,1).Range.Text += ((EA.Element)de.Key).Notes;}// Because we are including both the short description and the notes of the requirement// in the cell, we need to discriminate them. We do this by bolding the short description,// and reducing the font size of the notes to 8.Range wr = table.Cell(row,1).Range;wr.Sentences[1].Font.Bold = 1;for(int i=2; i<=wr.Sentences.Count; i++)wr.Sentences[i].Font.Size = 8;}}if ((valueType == DictionaryValueType.DVT_EA_ELEMENT) && (((EA.Element)de.Value) != null)){if (firstCellContent){firstCellContent = false;table.Cell(row,2).Range.Text = ((EA.Element)de.Value).Name;}else{table.Cell(row,2).Range.Text += ((EA.Element)de.Value).Name;}}else if ((valueType == DictionaryValueType.DVT_STRING) && (((string)de.Value).Length > 0)){if (firstCellContent){firstCellContent = false;table.Cell(row,2).Range.Text = (string)de.Value;}else{table.Cell(row,2).Range.Text += (string)de.Value;}}else{if (EA_DocGenOptions.optionValue(EA_DocGenOptions.boolean_options_e.SUPPRESS_UN_ALLOCATED_RELATIONSHIP_WARNINGS) == false){table.Cell(row,2).Range.Text = "Un-allocated relationship!";table.Cell(row,2).Range.Font.Color = WdColor.wdColorRed;}}}}/// <summary>/// This function will insert a relationship matrix table into the document, built from content/// specified in options that are provided in the notes section of the special element that/// has led to this function being called.////// The style of this single table is that required for requirement to requirement traceability./// Here, only a single table is generated, and the only elements in the left hand column will/// be those serialised to the document earlier on, and the content of the right hand column/// will be the parental requirements (all the way up to root requirements)./// </summary>/// <param name="EA_RelMatrix"></param>private static void processRelationshipMatrixElementStyle2(){int i;int tableNum = 0;Table table = null;// Scan the fromPackage to find all the "from elements".Main.WriteOutput(" Finding elements in fromPackage", -1);ElementAccumulator fromLister = new ElementAccumulator(RM_Matrix.from.elementTypes);EA_Utilities.findAndProcessPackageElements( fromPackage, fromLister, RM_Matrix.from.packageRecursion );if (createWordDoc.abortCreationThread)return;// Remove all elements from the list that did not go to form content in the document// prior to this point in the document generation process.for (i=0; i<fromLister.Elements.Count; ){if (createWordDoc.abortCreationThread)return;if ( !processedElements.Contains( ((EA.Element)fromLister.Elements[i]).ElementID ) ){fromLister.Elements.RemoveAt(i);continue;}i++;}if (createWordDoc.abortCreationThread)return;// Sort the remaining "from elements"Main.WriteOutput(" Sorting elements in fromPackage", -1);if (RM_Matrix.from.reqTagSort){elementSortByTAG sorter = new elementSortByTAG();fromLister.Elements.Sort( sorter );}else{elementSortByName sorter = new elementSortByName();fromLister.Elements.Sort( sorter );}if (createWordDoc.abortCreationThread)return;// Scan the toPackage to find all the "to elements".// Extract their element IDs into a new list.Main.WriteOutput(" Finding elements in toPackage", -1);ElementAccumulator toLister = new ElementAccumulator(RM_Matrix.to.elementTypes);EA_Utilities.findAndProcessPackageElements( toPackage, toLister, RM_Matrix.to.packageRecursion );ArrayList acceptableParents = new ArrayList();foreach(EA.Element ele in toLister.Elements){if (createWordDoc.abortCreationThread)return;acceptableParents.Add( ele.ElementID);}if (createWordDoc.abortCreationThread)return;// The list of from elements dictates the number of rows in our table.int numberOfFromToRows = fromLister.Elements.Count;Main.WriteOutput(" Creating from-to table", -1);if (RM_Matrix.fromToTable.introText != null && RM_Matrix.fromToTable.introText.Length > 0){Range wr = TextualContent.appendAndSelectText( RM_Matrix.fromToTable.introText, EA_Constants.styleName_Body1 );object obj = string.Format("EA_DocGen Informational Comment - This table has been produced using the following options:\n\n{0}", RM_Element.Notes);createWordDoc.WordDocument.Comments.Add(wr, ref obj);}// create the from-to table in the word doctableNum = TabularContent.Table_Create( RM_Matrix.fromToTable.title, true, numberOfFromToRows + 1, 2 );table = createWordDoc.WordDocument.Tables[tableNum];TabularContent.Table_SetTableColumnTitle(table, RM_Matrix.from.columnTitle, 1);TabularContent.Table_SetTableColumnTitle(table, RM_Matrix.to.columnTitle, 2);int row = 2;foreach(EA.Element ele in fromLister.Elements){if (createWordDoc.abortCreationThread)return;table.Cell(row,1).Range.Text = ele.Name;if (RM_Matrix.from.elementNotes && ele.Notes != null && ele.Notes.Length > 0){table.Cell(row,1).Range.Text += ele.Notes;}StringBuilder sb = new StringBuilder();ArrayList parentsDiscovered = new ArrayList();// Find this requirement's parent (source) requirementselementParenthood(ele, ref acceptableParents, ref parentsDiscovered, ref sb);// if no source requirements found and user has opted for trimming, delete the table rowif (RM_Matrix.from.packageTrimming == true && sb.Length == 0){TabularContent.Table_DeleteThisRow(table, row);continue;}// fill in the right hand cell on this row of the table with the source requirement listtable.Cell(row,2).Range.Text = sb.ToString();row++;}}private static void elementParenthood(EA.Element ele, ref ArrayList acceptableParents, ref ArrayList parentsDiscovered, ref StringBuilder sb){foreach(EA.Connector con in ele.Connectors){if (createWordDoc.abortCreationThread)return;// ignore downwards direction, indicated by the client ID being equal to the element IDif (con.ClientID != ele.ElementID){// if we have not already come across this upward path before...if (!parentsDiscovered.Contains(con.ClientID)){parentsDiscovered.Add(con.ClientID);// if this parent is in the list of acceptable parents...if (acceptableParents.Contains(con.ClientID)){// get the parent element, add its name to our list, and ascend through the element// using recursion.EA.Element client = Main.EA_Repository.GetElementByID(con.ClientID);if (client != null){if (sb.Length > 0)sb.Append("\n");sb.Append(client.Name);elementParenthood(client, ref acceptableParents, ref parentsDiscovered, ref sb);}}}}}}}}