Subversion Repositories DevTools

Rev

Rev 2104 | Rev 2118 | Go to most recent revision | 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;

namespace EA_DocGen
{
        /// <summary>
        /// Summary description for EA_RelationshipMatrix.
        /// </summary>
        public class EA_RelationshipMatrix
        {
      private static ArrayList processedElements = null;

      // declare option variables
      private static int style = 1;

      private static string fromIntroText = null;     // This one is optional 
      private static string fromPackageGUID = null;
      private static string fromToTableTitle = null;  // This one is optional
      private static string fromColumnTitle = null;
      private static ArrayList fromElementTypes = null;
      private static bool fromPackageRecursion = true;
      private static EA.Package fromPackage = null;
      private static bool fromPackageTrimming = false;

      private static string toIntroText = null;       // This one is optional
      private static string toPackageGUID = null;
      private static string toFromTableTitle = null;  // This one is optional
      private static string toColumnTitle = null;
      private static ArrayList toElementTypes = null;
      private static bool toPackageRecursion = true;
      private static EA.Package toPackage = null;
      private static bool toPackageTrimming = false;

      

      private static string [] options_string = null;



                public static void initialise(ArrayList processedElementsRef)
                {
         processedElements = processedElementsRef;

         fromElementTypes  = new ArrayList();
         toElementTypes    = new ArrayList();
                }

      private static void reset()
      {
         style = 1;

         fromIntroText = null;
         fromPackageGUID = null;
         fromToTableTitle = null;
         fromColumnTitle = null;
         fromElementTypes.Clear();
         fromPackageRecursion = true;
         fromPackageTrimming = false;
         fromPackage = null;
         toIntroText = null;
         toPackageGUID = null;
         toFromTableTitle = null;
         toColumnTitle = null;
         toElementTypes.Clear();
         toPackageRecursion = true;
         toPackageTrimming = false;
         toPackage = null;
      }


      public static bool processRelationshipMatrixOptions(EA.Element theElement)
      {
         reset();

         // Extract the control options from the notes of the element
         
         string delimStr = "\n";
         char [] delim = delimStr.ToCharArray();
         options_string = theElement.Notes.ToString().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 required
         foreach(string s in options_string)
         {
            if (s.Length > 0 && s != "\n" && s != "\r" && s[0] != '\\')
            {
               if (s.StartsWith("style="))
               {
                  style = EA_DocGenOptions.getOptionValue(s, style);
               }
               // FROM items
               else if (s.StartsWith("fromIntroText="))
               {
                  fromIntroText = EA_DocGenOptions.getOptionValue(s, null);
               }
               else if (s.StartsWith("fromToTableTitle=")) 
               {
                  fromToTableTitle = EA_DocGenOptions.getOptionValue(s, null);
               }
               else if (s.StartsWith("fromColumnTitle=")) 
               {
                  fromColumnTitle = EA_DocGenOptions.getOptionValue(s, fromColumnTitle);
               }
               else if (s.StartsWith("fromPackageRecursion=")) 
               {
                  fromPackageRecursion = EA_DocGenOptions.getOptionValue(s, fromPackageRecursion);
               }
               else if (s.StartsWith("fromPackageTrimming="))
               {
                  fromPackageTrimming = EA_DocGenOptions.getOptionValue(s, fromPackageTrimming);
               }
               else if (s.StartsWith("fromPackage=")) 
               {
                  fromPackageGUID = EA_DocGenOptions.getOptionValue(s, fromPackageGUID);
               }
               else if (s.StartsWith("fromElementType=")) 
               {
                  string et = EA_DocGenOptions.getOptionValue(s,null);
                  if (et != null) 
                  {
                     fromElementTypes.Add( et );
                  }
               }

                  // TO items
               else if (s.StartsWith("toIntroText="))
               {
                  toIntroText = EA_DocGenOptions.getOptionValue(s, null);
               }
               else if (s.StartsWith("toFromTableTitle=")) 
               {
                  toFromTableTitle = EA_DocGenOptions.getOptionValue(s, null);
               }
               else if (s.StartsWith("toColumnTitle=")) 
               {
                  toColumnTitle = EA_DocGenOptions.getOptionValue(s, toColumnTitle);
               }
               else if (s.StartsWith("toPackageRecursion=")) 
               {
                  toPackageRecursion = EA_DocGenOptions.getOptionValue(s, toPackageRecursion);
               }
               else if (s.StartsWith("toPackageTrimming=")) 
               {
                  toPackageTrimming = EA_DocGenOptions.getOptionValue(s, toPackageTrimming);
               }               
               else if (s.StartsWith("toPackage=")) 
               {
                  toPackageGUID = EA_DocGenOptions.getOptionValue(s, toPackageGUID);
               }
               else if (s.StartsWith("toElementType=")) 
               {
                  string et = EA_DocGenOptions.getOptionValue(s,null);
                  if (et != null) 
                  {
                     toElementTypes.Add( et );
                  }
               }
            }
         }

         // Verify that we have all the necessary compulsory options
         if (fromToTableTitle == null && toFromTableTitle == null)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option: Must specify at least one table title");
            return false;
         }

         // FROM items
         if (fromColumnTitle == null)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option: fromColumnTitle");
            return false;
         }
         if (fromPackageGUID == null)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option: fromPackageGUID");
            return false;
         }
         if (fromElementTypes.Count == 0)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option(s): fromElementTypes");
            return false;
         }      

         // TO items
         if (toColumnTitle == null)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option: toColumnTitle");
            return false;
         }
         if (toPackageGUID == null)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option: toPackageGUID");
            return false;
         }
         if (toElementTypes.Count == 0)
         {
            MessageBox.Show("Error in EA_DocGenRelationshipMatrix options list\n\n" +
               "Missing option(s): toElementTypes");
            return false;
         }   

         // Find GUID linked packages in the repository
         fromPackage = Main.EA_Repository.GetPackageByGuid( fromPackageGUID );
         if (fromPackage == null)
         {
            MessageBox.Show("Error processing EA_DocGenRelationshipMatrix\n\n" +
               "could not locate fromPackage in Repository");
            return false;
         }

         toPackage = Main.EA_Repository.GetPackageByGuid( toPackageGUID );
         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"

               + "toFromTableTitle=\r\n"
               + "toIntroText=\r\n"
               + "toColumnTitle=\r\n"
               + "toPackage=\r\n"
               + "toElementType=\r\n"
               + "toPackageRecursion=true\r\n"
               + "toPackageTrimming=false\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"

               + "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 )
      {
         switch (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;
         Word.Table table = null;

         // Scan the fromPackage to find all the "from elements".
         ElementAccumulator fromLister = new ElementAccumulator(fromElementTypes);
         EA_Utilities.findAndProcessPackageElements( fromPackage, fromLister, fromPackageRecursion );

         if (createWordDoc.abortCreationThread)
            return;

         // Sort the "from elements"
         elementSortByName sorter = new elementSortByName();
         fromLister.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 (fromToTableTitle != null && fromToTableTitle.Length > 0)
            needFromToTable = true;

         bool needToFromTable = false;
         if (toFromTableTitle != null && toFromTableTitle.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;
         
         foreach(EA.Element fromElement in fromLister.Elements)
         {
            if (createWordDoc.abortCreationThread)
               return;

            // look at the elements connection collection to find references to the
            // destination elements
            bool 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 connections
               if (fromElement.ElementID == thisCon.SupplierID)
                  continue;

               // if the destination element is of a type that the user has requested to include...
               if (!toElementTypes.Contains( destE.Type ))
                  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 (fromPackageTrimming == false)
            {
               DictionaryEntry newFromToEntry = new DictionaryEntry(fromElement, null);
               fromToDictionary.Add( newFromToEntry );
               numberOfFromToRows++;
            }
         }
         
         if (createWordDoc.abortCreationThread)
            return;

         if (needFromToTable)
         {
            // Now we can actually serialise the table

            if (fromIntroText != null && fromIntroText.Length > 0)
            {
               TextualContent.appendAndSelectText( fromIntroText, EA_Constants.styleName_Body1 );
            }

            // create the from-to table in the word doc
            tableNum = TabularContent.Table_Create( fromToTableTitle, true, numberOfFromToRows + 1, 2 );
            table = createWordDoc.WordDocument.Tables[tableNum];
            
            TabularContent.Table_SetTableColumnTitle(table, fromColumnTitle, 1);
            TabularContent.Table_SetTableColumnTitle(table, toColumnTitle, 2);

            fillInTable(fromToDictionary, table);
         }

         if (createWordDoc.abortCreationThread)
            return;

         // Does user want a to-from table ?
         if (needToFromTable)
         {
            // re-use the fromToDictionary to prepare the to-from table content 
            fromToDictionary.Clear();

            // find all elements for the left hand column of the to-from table.
            ElementAccumulator toLister = new ElementAccumulator(toElementTypes);
            EA_Utilities.findAndProcessPackageElements( toPackage, toLister, toPackageRecursion );

            if (createWordDoc.abortCreationThread)
               return;

            // Sort the "to" elements
            toLister.Elements.Sort( sorter );

            if (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 content
               foreach (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 (toPackageTrimming == 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 document
            if (toIntroText != null && toIntroText.Length > 0)
            {
               TextualContent.appendAndSelectText( toIntroText, EA_Constants.styleName_Body1 );
            }

            // create the table in the word doc
            tableNum = TabularContent.Table_Create( toFromTableTitle, true, numberOfFromToRows + 1, 2 );
            table = createWordDoc.WordDocument.Tables[tableNum];
            
            TabularContent.Table_SetTableColumnTitle(table, toColumnTitle, 1);
            TabularContent.Table_SetTableColumnTitle(table, fromColumnTitle, 2);
 
            fillInTable(fromToDictionary, table);
         }
      }

      private static void fillInTable(ArrayList fromToDictionary, Word.Table table )
      {
         int lastFromElementId = -1;
         bool firstToFromCon = 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;
               firstToFromCon = true;
            }
               
            if (((string)de.Value).Length > 0)
            {
               if (firstToFromCon)
               {
                  firstToFromCon = false;
                  table.Cell(row,2).Range.Text = (string)de.Value;
               }
               else
               {
                  table.Cell(row,2).Range.Text += (string)de.Value;
               }
            }
            else
            {
               if (EA_DocGenOptions.opt_SuppressUnAllocatedRelationshipWarnings == false)
               {
                  table.Cell(row,2).Range.Text = "Un-allocated relationship!";
                  table.Cell(row,2).Range.Font.Color = Word.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;
         Word.Table table = null;

         // Scan the fromPackage to find all the "from elements".
         ElementAccumulator fromLister = new ElementAccumulator(fromElementTypes);
         EA_Utilities.findAndProcessPackageElements( fromPackage, fromLister, fromPackageRecursion );

         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"
         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.
         ElementAccumulator toLister = new ElementAccumulator(toElementTypes);
         EA_Utilities.findAndProcessPackageElements( toPackage, toLister, toPackageRecursion );
         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;
         
         if (fromIntroText != null && fromIntroText.Length > 0)
         {
            TextualContent.appendAndSelectText( fromIntroText, EA_Constants.styleName_Body1 );
         }

         // create the from-to table in the word doc
         tableNum = TabularContent.Table_Create( fromToTableTitle, true, numberOfFromToRows + 1, 2 );
         table = createWordDoc.WordDocument.Tables[tableNum];
            
         TabularContent.Table_SetTableColumnTitle(table, fromColumnTitle, 1);
         TabularContent.Table_SetTableColumnTitle(table, toColumnTitle, 2);

         int row = 2;
         foreach(EA.Element ele in fromLister.Elements)
         {
            if (createWordDoc.abortCreationThread)
               return;

            table.Cell(row,1).Range.Text = ele.Name;

            StringBuilder sb = new StringBuilder();

            ArrayList parentsDiscovered = new ArrayList();

            // Find this requirement's parent (source) requirements
            elementParenthood(ele, ref acceptableParents, ref parentsDiscovered, ref sb);

            // if no source requirements found and user has opted for trimming, delete the table row
            if (fromPackageTrimming == 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 list
            table.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 ID
            if (con.ClientID != ele.ElementID)
            {
               // if we have not already come across this upward path before...
               if (!parentsDiscovered.Contains(con.ClientID))
               {
                  parentsDiscovered.Add(con.ClientID);

                  // is 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);
                     }
                  }
               }
            }
         }
      }

        }
}