Rev 2090 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
using System;using System.Drawing;using System.Collections;using System.ComponentModel;using System.Windows.Forms;using Word;namespace EA_DocGen{/// <summary>/// Summary description for createWordDoc./// </summary>public class createWordDoc : System.Windows.Forms.Form{// Enterprise Architect Model related dataprivate EA.Repository EA_Repository = null;private EA.Package EA_ParentPackage = null;private EA.Project EA_Project;private EA_Utilities EA_Utils = null;private bool oneShot_skipRootPackage = false;// MS-Office Word related dataprivate Word.Application WordApp = null;private Word.Document WordDocument = null;private Word.Range WordRange;private object startLocation;private object endLocation;private int[] DocSection = null;private int iDocSection = 0;private System.Windows.Forms.TextBox textBox_template;private System.Windows.Forms.Button button_browse_template;private System.Windows.Forms.TextBox textBox_output_file;private System.Windows.Forms.Button button_browse_output_file;private System.Windows.Forms.Button button_cancel;private System.Windows.Forms.Button button_generate;private System.Windows.Forms.Button button_view_output;private System.Windows.Forms.Label label1;private System.Windows.Forms.Label label2;private System.Windows.Forms.TextBox textBox_progress;private System.Windows.Forms.Label label3;private System.Windows.Forms.CheckBox checkBox_WordVisibility;/// <summary>/// Required designer variable./// </summary>private System.ComponentModel.Container components = null;public createWordDoc(EA.Repository theRepository, EA.Package theParentPackage, EA_Utilities theEA_Utils){//// Required for Windows Form Designer support//InitializeComponent();EA_Repository = theRepository;EA_ParentPackage = theParentPackage;EA_Project = EA_Repository.GetProjectInterface();EA_Utils = theEA_Utils;DocSection = new int[12];for (int i=0; i<DocSection.Length; i++){DocSection[i] = 0;}WordApp = new Word.Application();}/// <summary>/// Clean up any resources being used./// </summary>protected override void Dispose( bool disposing ){if( disposing ){if(components != null){components.Dispose();}}base.Dispose( disposing );}#region Windows Form Designer generated code/// <summary>/// Required method for Designer support - do not modify/// the contents of this method with the code editor./// </summary>private void InitializeComponent(){this.textBox_template = new System.Windows.Forms.TextBox();this.button_browse_template = new System.Windows.Forms.Button();this.textBox_output_file = new System.Windows.Forms.TextBox();this.button_browse_output_file = new System.Windows.Forms.Button();this.button_cancel = new System.Windows.Forms.Button();this.button_generate = new System.Windows.Forms.Button();this.button_view_output = new System.Windows.Forms.Button();this.label1 = new System.Windows.Forms.Label();this.label2 = new System.Windows.Forms.Label();this.textBox_progress = new System.Windows.Forms.TextBox();this.label3 = new System.Windows.Forms.Label();this.checkBox_WordVisibility = new System.Windows.Forms.CheckBox();this.SuspendLayout();//// textBox_template//this.textBox_template.Location = new System.Drawing.Point(16, 40);this.textBox_template.Name = "textBox_template";this.textBox_template.Size = new System.Drawing.Size(592, 22);this.textBox_template.TabIndex = 0;this.textBox_template.Text = "";this.textBox_template.TextChanged += new System.EventHandler(this.textBox_template_TextChanged);//// button_browse_template//this.button_browse_template.Location = new System.Drawing.Point(616, 40);this.button_browse_template.Name = "button_browse_template";this.button_browse_template.Size = new System.Drawing.Size(80, 23);this.button_browse_template.TabIndex = 1;this.button_browse_template.Text = "Browse";this.button_browse_template.Click += new System.EventHandler(this.button_browse_template_Click);//// textBox_output_file//this.textBox_output_file.Location = new System.Drawing.Point(16, 112);this.textBox_output_file.Name = "textBox_output_file";this.textBox_output_file.Size = new System.Drawing.Size(592, 22);this.textBox_output_file.TabIndex = 2;this.textBox_output_file.Text = "";this.textBox_output_file.TextChanged += new System.EventHandler(this.textBox_output_file_TextChanged);//// button_browse_output_file//this.button_browse_output_file.Location = new System.Drawing.Point(616, 112);this.button_browse_output_file.Name = "button_browse_output_file";this.button_browse_output_file.Size = new System.Drawing.Size(80, 24);this.button_browse_output_file.TabIndex = 3;this.button_browse_output_file.Text = "Browse";this.button_browse_output_file.Click += new System.EventHandler(this.button_browse_output_file_Click);//// button_cancel//this.button_cancel.Location = new System.Drawing.Point(616, 384);this.button_cancel.Name = "button_cancel";this.button_cancel.Size = new System.Drawing.Size(80, 23);this.button_cancel.TabIndex = 4;this.button_cancel.Text = "Close";this.button_cancel.Click += new System.EventHandler(this.button_cancel_Click);//// button_generate//this.button_generate.Location = new System.Drawing.Point(496, 384);this.button_generate.Name = "button_generate";this.button_generate.Size = new System.Drawing.Size(96, 24);this.button_generate.TabIndex = 5;this.button_generate.Text = "Generate";this.button_generate.Click += new System.EventHandler(this.button_generate_Click);//// button_view_output//this.button_view_output.Location = new System.Drawing.Point(376, 384);this.button_view_output.Name = "button_view_output";this.button_view_output.Size = new System.Drawing.Size(96, 23);this.button_view_output.TabIndex = 6;this.button_view_output.Text = "View Output";this.button_view_output.Click += new System.EventHandler(this.button_view_output_Click);//// label1//this.label1.Location = new System.Drawing.Point(16, 16);this.label1.Name = "label1";this.label1.Size = new System.Drawing.Size(224, 23);this.label1.TabIndex = 7;this.label1.Text = "Input Template or Document Name";//// label2//this.label2.Location = new System.Drawing.Point(16, 88);this.label2.Name = "label2";this.label2.Size = new System.Drawing.Size(280, 23);this.label2.TabIndex = 8;this.label2.Text = "Output Document Name";//// textBox_progress//this.textBox_progress.Location = new System.Drawing.Point(16, 184);this.textBox_progress.Multiline = true;this.textBox_progress.Name = "textBox_progress";this.textBox_progress.ReadOnly = true;this.textBox_progress.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;this.textBox_progress.Size = new System.Drawing.Size(680, 168);this.textBox_progress.TabIndex = 9;this.textBox_progress.Text = "";//// label3//this.label3.Location = new System.Drawing.Point(16, 152);this.label3.Name = "label3";this.label3.TabIndex = 10;this.label3.Text = "Progress";//// checkBox_WordVisibility//this.checkBox_WordVisibility.Location = new System.Drawing.Point(16, 384);this.checkBox_WordVisibility.Name = "checkBox_WordVisibility";this.checkBox_WordVisibility.Size = new System.Drawing.Size(256, 24);this.checkBox_WordVisibility.TabIndex = 17;this.checkBox_WordVisibility.Text = "Show MS-WORD During Construction";//// createWordDoc//this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);this.ClientSize = new System.Drawing.Size(712, 432);this.Controls.Add(this.checkBox_WordVisibility);this.Controls.Add(this.label3);this.Controls.Add(this.textBox_progress);this.Controls.Add(this.textBox_output_file);this.Controls.Add(this.textBox_template);this.Controls.Add(this.label2);this.Controls.Add(this.label1);this.Controls.Add(this.button_view_output);this.Controls.Add(this.button_generate);this.Controls.Add(this.button_cancel);this.Controls.Add(this.button_browse_output_file);this.Controls.Add(this.button_browse_template);this.Name = "createWordDoc";this.Text = "createWordDoc";this.Closing += new System.ComponentModel.CancelEventHandler(this.createWordDoc_Closing);this.ResumeLayout(false);}#endregion#region Message handlersprivate void button_view_output_Click(object sender, System.EventArgs e){if (WordApp != null){WordApp.Visible = true;}}private void button_generate_Click(object sender, System.EventArgs e){if (this.textBox_template.Text.Length > 0){if (this.textBox_output_file.Text.Length > 0){createTheWordDoc();}else{MessageBox.Show("Error - must specify an output file");}}else{MessageBox.Show("Error - must specify an input template file");}}private void button_cancel_Click(object sender, System.EventArgs e){this.Close();}private void button_browse_template_Click(object sender, System.EventArgs e){OpenFileDialog dialog = new OpenFileDialog();dialog.Filter = "Word Files (*.doc;*.dot)|*.doc;*.dot|All files(*.*)|*.*";dialog.FilterIndex = 1;dialog.ShowDialog();this.textBox_template.Text = dialog.FileName;}private void button_browse_output_file_Click(object sender, System.EventArgs e){OpenFileDialog dialog = new OpenFileDialog();dialog.Filter = "Word Files (*.doc;*.dot)|*.doc;*.dot|All files(*.*)|*.*";dialog.FilterIndex = 1;dialog.CheckFileExists = false;dialog.ShowDialog();this.textBox_output_file.Text = dialog.FileName;}private void textBox_template_TextChanged(object sender, System.EventArgs e){}private void textBox_output_file_TextChanged(object sender, System.EventArgs e){}private void createWordDoc_Closing(object sender, System.ComponentModel.CancelEventArgs e){if (WordApp != null){// try-catch just in case user closed the application themselves whilst viewing the// generated output documenttry{object nothing = Type.Missing;object notTrue = false;WordApp.Application.Quit( ref notTrue, ref nothing, ref nothing);}catch (Exception createWordDoc_Closing_exception){// dummy statementString s = createWordDoc_Closing_exception.Message;}}}#endregion#region word document generation code/// <summary>/// This class is one derived from the EA_UtilitiesRecursionWorker base class so that/// an instance of it can be used as a parameter in the EA_Utilities.findAndProcessPackageElements/// method which recursively parses a given package. Thus, clients can parse EA model structure but/// specify their own functionality to be carried out on packages and elements found in the/// parsing./// This particular derived class is designed to collect a list of element names from the/// package, from elements that have a type that is acceptable./// </summary>public class ElementAccumulator : EA_UtilitiesRecursionWorker{public ArrayList Elements = null;private ArrayList validElementTypes = null;public ElementAccumulator(ArrayList elementTypeList): base(){validElementTypes = elementTypeList;Elements = new ArrayList();}public override void processElement( EA.Element theElement ){if (validElementTypes.Contains( theElement.Type )){if ( (theElement.Type == "Requirement") && (theElement.Status == "Rejected") )return;Elements.Add( theElement );}}}private void trackDocSection(int recurse_level){iDocSection = recurse_level;DocSection[iDocSection]++;for (int i=iDocSection+1; i<DocSection.Length; i++)DocSection[i] = 0;}/// <summary>/// Displays progress of the document generation in the createWordDocument dialog/// </summary>/// <param name="prefix"></param>/// <param name="EA_string"></param>private void displayProgress(string prefix, string EA_string){textBox_progress.AppendText( prefix + EA_string + "\r\n" );// Display to the output tab as well - as a longer term record of what happenedstring s = "";for (int i = 1; i<=iDocSection; i++){if (i!=1)s = s + ".";s = s + DocSection[i].ToString();}while (s.Length < 30)s += " ";EA_Repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME, s + prefix + EA_string, 0);}/// <summary>/// This method takes the specified EA diagram element and puts the diagram image/// into the clipboard from where it can be pasted into the word document. After/// pasting it into the document, it sets the style and other aspects of the image./// </summary>/// <param name="theDiagram"></param>private void appendAndSelectDiagramViaClipboard(EA.Diagram theDiagram){// open the diagram in EA and copy it's image to the clipboardif (EA_Project.PutDiagramImageOnClipboard(theDiagram.DiagramGUID,0)){// create a range at the end of the documentstartLocation = WordDocument.Content.End;WordDocument.Content.InsertParagraphAfter();endLocation = WordDocument.Content.End;WordRange = WordDocument.Range(ref startLocation, ref endLocation);object direction = Word.WdCollapseDirection.wdCollapseEnd;WordRange.Collapse(ref direction); // collapse prevents existing content being replaced// Get ready for the diagram paste and the formatting we want to do on it with the// selection object.SelectInsertionPointAtEndOfDocument();// Paste the diagram into the documentWordRange.Paste();// Set style of the diagram to "Normal" so that it can occupy space all the way to the left marginobject l_style = "Normal";endLocation = WordDocument.Content.End;WordRange = WordDocument.Range(ref startLocation, ref endLocation);WordRange.set_Style(ref l_style);// Center the diagram on the pageobject unit = Word.WdUnits.wdLine;object missing = Type.Missing;object count = 1;WordApp.Selection.MoveDown( ref unit, ref count, ref missing);WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;// Add a captionWordDocument.Content.InsertParagraphAfter();SelectInsertionPointAtEndOfDocument();object Label = "Figure";object Title = Type.Missing;object TitleAutoText = Type.Missing;object Position = Word.WdCaptionPosition.wdCaptionPositionAbove;object ExcludeLabel = 0;WordApp.Selection.InsertCaption( ref Label, ref Title, ref TitleAutoText, ref Position, ref ExcludeLabel);WordApp.Selection.TypeText( ": " + theDiagram.Name.ToString());WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;// experimental code to create a bookmark for the figure caption//WordDocument.Paragraphs[ WordDocument.Paragraphs.Count ].Range.Select();//object lastParaRange = WordDocument.Paragraphs[ WordDocument.Paragraphs.Count ].Range;//string bookmarkName = theDiagram.Name;//bookmarkName = bookmarkName.Replace(' ', '_');//WordDocument.Bookmarks.Add( bookmarkName, ref lastParaRange );// Close the diagram in EAEA_Repository.CloseDiagram(theDiagram.DiagramID);}}/// <summary>/// Appends a specified text string to the word document, selects the new text, and applies/// the specified style formatting to it./// </summary>/// <param name="wordText"></param>/// <param name="styleText"></param>/// <param name="continuation"></param>private void appendAndSelectText(string wordText, string styleText){appendAndSelectText(wordText, styleText, false );}private void appendAndSelectText(string wordText, string styleText, bool continuation ){if (wordText.Length > 0){object style = styleText;int i;startLocation = 0;endLocation = i = WordDocument.Content.End;WordRange = WordDocument.Range(ref startLocation, ref endLocation);if (!continuation)WordRange.InsertAfter( "\n" );WordRange.InsertAfter( wordText );// Make a range out of the pasted textstartLocation = i;endLocation = WordDocument.Content.End;WordRange = WordDocument.Range(ref startLocation, ref endLocation);// and set the pasted text styleWordRange.set_Style(ref style);}}/// <summary>/// Appends a specified text string to the word document, selects the new text, and applies/// a heading level style to it./// </summary>/// <param name="wordText"></param>/// <param name="level"></param>private void appendAndSelectHeadingText(string wordText, int level){// Convert level to heading styleString styleText;switch(level){case 1: styleText = "Heading 1"; break;case 2: styleText = "Heading 2"; break;case 3: styleText = "Heading 3"; break;case 4: styleText = "Heading 4"; break;case 5: styleText = "Heading 5"; break;case 6: styleText = "Heading 6"; break;case 7: styleText = "Heading 7"; break;case 8: styleText = "Heading 8"; break;case 9: styleText = "Heading 9"; break;default: styleText = "Body 1"; break;}// append the text as a headingappendAndSelectText(wordText, styleText);}/// <summary>/// Appends a specified text string to the word document, selects the new text, and applies/// a num para heading level style to it./// </summary>/// <param name="wordText"></param>/// <param name="level"></param>private void appendAndSelectNumParaText(string wordText, int level){// Convert level to heading styleString styleText;switch(level){case 1: styleText = "NumPara 1"; break;case 2: styleText = "NumPara 2"; break;case 3: styleText = "NumPara 3"; break;case 4: styleText = "NumPara 4"; break;case 5: styleText = "NumPara 5"; break;case 6: styleText = "NumPara 6"; break;case 7: styleText = "NumPara 7"; break;case 8: styleText = "NumPara 8"; break;case 9: styleText = "NumPara 9"; break;default: styleText = "Body 1"; break;}// append the text as a headingappendAndSelectText(wordText, styleText);}private void SelectInsertionPointAtEndOfDocument(){object unit;object extend;unit = Word.WdUnits.wdStory;extend = Word.WdMovementType.wdMove;WordApp.Selection.EndKey(ref unit, ref extend);}/// <summary>/// This is an attempt to create a table similar in style to those made by the ERG doc template macros./// It is not exaclty that same though, and further work could be done to align it more precisely./// </summary>/// <param name="tableTitle"></param>/// <param name="numRows"></param>/// <param name="numCols"></param>/// <returns></returns>private int createTable(string tableTitle, int numRows, int numCols){WordDocument.Content.InsertParagraphAfter();SelectInsertionPointAtEndOfDocument();object Label = "Table";object Title = Type.Missing;object TitleAutoText = Type.Missing;object Position = Word.WdCaptionPosition.wdCaptionPositionAbove;object ExcludeLabel = 0;WordApp.Selection.InsertCaption( ref Label, ref Title, ref TitleAutoText, ref Position, ref ExcludeLabel);WordApp.Selection.TypeText( ": " + tableTitle);WordApp.Selection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;startLocation = WordDocument.Content.End;WordDocument.Content.InsertParagraphAfter();endLocation = WordDocument.Content.End;WordRange = WordDocument.Range(ref startLocation, ref endLocation);object defaultTableBehaviour = Type.Missing;object autofitBehaviour = Type.Missing;Word.Table Table = WordDocument.Tables.Add( WordRange, numRows, numCols, ref defaultTableBehaviour, ref autofitBehaviour );Table.Rows[1].Shading.BackgroundPatternColor = Word.WdColor.wdColorGray10;Table.Borders.OutsideLineStyle = Word.WdLineStyle.wdLineStyleSingle;Table.Borders.InsideLineStyle = Word.WdLineStyle.wdLineStyleSingle;Table.Select();object tableTextStyle = "Table Text";Table.Range.set_Style( ref tableTextStyle );return WordDocument.Tables.Count;}/// <summary>/// This function process a package link element, parsing each linked package by the/// parse_package() recursive function./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>private void processPackageLink(EA.Element theElement, int recurse_level){string [] EA_DocGenPkgLnk = null;string delimStr = "\r\n";char [] delim = delimStr.ToCharArray();EA_DocGenPkgLnk = theElement.Notes.ToString().Split(delim,100);string linkedName = "";foreach(string s in EA_DocGenPkgLnk){if (s.Length > 0 && s != "\n" && s != "\r"){if (s == "skiproot"){oneShot_skipRootPackage = true;}else if (s[0] == '{'){EA.Package theFoundPackage = EA_Utils.EA_Finder.findPackageInRepositoryByGUID(s);if (theFoundPackage != null){parse_package(theFoundPackage, recurse_level);}else{MessageBox.Show("WARNING - Could not find linked package : " + linkedName );}}else{linkedName = s;}}}}/// <summary>/// This function process a diagram link element, using the appendAndSelectDiagramViaClipboard()/// for each linked diagram./// </summary>/// <param name="theElement"></param>private void processDiagramLink(EA.Element theElement){string [] EA_DocGenDiagLnk = null;string delimStr = "\r\n";char [] delim = delimStr.ToCharArray();EA_DocGenDiagLnk = theElement.Notes.ToString().Split(delim,100);string linkedName = "";foreach(string s in EA_DocGenDiagLnk){if (s.Length > 0 && s != "\n" && s != "\r" ){if (s[0] == '{'){EA.Diagram theFoundDiagram = EA_Utils.EA_Finder.findDiagramInRepositoryByGUID(s);if (theFoundDiagram != null){displayProgress( "DIAGRAM: ", theFoundDiagram.Name.ToString() );appendAndSelectDiagramViaClipboard( theFoundDiagram );}else{MessageBox.Show("WARNING - Could not find linked diagram : " + linkedName );}}else{linkedName = s;}}}}/// <summary>/// This function process an element link element, parsing each linked element by the/// parse_element() recursive function./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>private void processElementLink(EA.Element theElement, int recurse_level ){string [] EA_DocGenEleLnk = null;string delimStr = "\r\n";char [] delim = delimStr.ToCharArray();EA_DocGenEleLnk = theElement.Notes.ToString().Split(delim,100);string linkedName = "";foreach(string s in EA_DocGenEleLnk){if (s.Length > 0 && s != "\n" && s != "\r"){if ( s[0] == '{'){EA.Element theFoundElement = EA_Utils.EA_Finder.findElementInRepositoryByGUID(s);if (theFoundElement != null){parse_element( theFoundElement, recurse_level );}else{MessageBox.Show("WARNING - Could not find linked element : " + linkedName );}}else{linkedName = s;}}}}/// <summary>/// This method creates a table from the content of an EA_DocGenTable element in the model./// The elements notes section contains the table parameters and cell content./// See the EA_Utilities::AddTableElement() method for the layout of this content./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>private void processTableElement(EA.Element theElement, int recurse_level ){string [] EA_DocGenTable = null;string [] cells = null;string delimStr = "\r\n";char [] delim = delimStr.ToCharArray();string trimStr = " ";char [] trimChars = trimStr.ToCharArray();EA_DocGenTable = theElement.Notes.ToString().Split(delim,200);int columnCount = 0;int rowCount = 0;char seperator = ',';string tableTitle = "";bool gotSeperator = false;bool gotTitle = false;// Scan the notes content line by line looking for table parameters and counting// the number of table rows (which is not specified as an explicit table parameter).int s_ElementsConsumedByTableParams = 0;foreach(string s in EA_DocGenTable){if (s.Length > 0 && s != "\n" && s != "\r" ){if (gotTitle == false && s.StartsWith("title")){s_ElementsConsumedByTableParams++;tableTitle = EA_Utils.options.getOptionValue(s, tableTitle);gotTitle = true;if (tableTitle == ""){MessageBox.Show( "Table Element Serialisation Failed - Bad Title" );break;}}else if (columnCount == 0 && s.StartsWith("columns")){s_ElementsConsumedByTableParams++;columnCount = EA_Utils.options.getOptionValue(s, columnCount);if (columnCount == 0){MessageBox.Show( "Table Element Serialisation Failed - bad column count" );break;}}else if (gotSeperator == false && s.StartsWith("seperator")){s_ElementsConsumedByTableParams++;seperator = EA_Utils.options.getOptionValue(s, seperator);gotSeperator = true;}else{rowCount++;}}}if (gotTitle == true && gotSeperator == true && columnCount != 0){if (rowCount < 2){MessageBox.Show( "Table Element Serialisation Failed - Insufficient Rows" );}else{// create the table in the word docint tableNum = createTable( tableTitle, rowCount, columnCount );Word.Table table = WordDocument.Tables[tableNum];object center = Word.WdParagraphAlignment.wdAlignParagraphCenter;// scan the element notes again to extract the cell content and add it to the// table we just created.int row = 1;int col = 1;foreach(string s in EA_DocGenTable){if (s.Length > 0 && s != "\n" && s != "\r" ){if (s_ElementsConsumedByTableParams > 0){s_ElementsConsumedByTableParams--;}else{delimStr = seperator.ToString();delim = delimStr.ToCharArray();cells = s.Split(delim,columnCount);if (cells.Length != columnCount){MessageBox.Show( "Table Element Serialisation Failed - Bad Row" );break;}else{// serialise table rowfor(col=1; col<=columnCount; col++){cells[col-1].TrimStart( trimChars );cells[col-1].TrimEnd( trimChars );table.Cell(row,col).Range.Text = cells[col-1];// special handling for heading rowif (row == 1){table.Cell(row,col).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(row,col).Range.Font.Bold = 1;}}row++;}}}}}}else{MessageBox.Show( "Table Element Serialisation Failed - Table Parameters Incomplete" );}}/// <summary>/// This function process a text element. It is a simple wrapper for the/// appendAndSelectText()function./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>private void processTextElement(EA.Element theElement, int recurse_level ){appendAndSelectText( theElement.Notes.ToString(), "Body 1" );}/// <summary>/// This function processes elements as if they represented document references for/// the References section of a document./// </summary>/// <param name="thePackage"></param>private void createReferencesSection(EA.Package thePackage){foreach(EA.Element theElement in thePackage.Elements){// Here we want to apply the special ERG template styles for each reference// item.appendAndSelectText( theElement.Name.ToString(), "Reference List Number" );appendAndSelectText( theElement.Notes.ToString(), "Reference List Text" );}}private void createTerminologySection(EA.Package thePackage){// Use local package elements as source of glossary, but if none are present, resort to using// the project glossary in the repositoryint numItems = thePackage.Elements.Count;if (numItems == 0){numItems = EA_Repository.Terms.Count;}if (numItems > 0){// Create a tableint tableNum = createTable("Terminology", numItems+1, 2);Word.Table table = WordDocument.Tables[tableNum];object center = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,1).Range.Text = "Term";table.Cell(1,1).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,1).Range.Font.Bold = 1;table.Cell(1,2).Range.Text = "Definition";table.Cell(1,2).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,2).Range.Font.Bold = 1;table.Columns[1].SetWidth(100, Word.WdRulerStyle.wdAdjustSameWidth );int row = 2;if (thePackage.Elements.Count > 0){// The advantage of using the package content is that the elements come out alphabetically// sorted already (assuming user has not over-riden the sort order in the project browser).// Also, the glossary content can be exactly tailored to what the document requires.foreach(EA.Element theElement in thePackage.Elements){table.Cell(row,1).Range.Text = theElement.Name.ToString();table.Cell(row,2).Range.Text = theElement.Notes.ToString();row++;}}else // default to using the project glossary in the repository{// TODO// 1. Alphabetically Sort the glossary terms before adding them to the table// The Repository glossary seems to be provided in non-sorted order unfortunately.// inject the glossary into the tableforeach(EA.Term theTerm in EA_Repository.Terms){table.Cell(row,1).Range.Text = theTerm.Term;table.Cell(row,2).Range.Text = theTerm.Meaning;row++;}}}}private bool generatePackageHeadingAndDescription( EA.Package thePackage, int recurse_level ){// disarm the one-shot package skipping feature if it was armed, and exit the function.if (oneShot_skipRootPackage == true){oneShot_skipRootPackage = false;return false;}// The package name is a heading, and the package notes are paragraphs// directly under that headingdisplayProgress( "PACKAGE: ", thePackage.Name.ToString() );appendAndSelectHeadingText( thePackage.Name.ToString(), recurse_level );// Special handling for package called "Terminology"if (thePackage.Name == "Terminology"){appendAndSelectText( thePackage.Notes.ToString(), "Body 1" );createTerminologySection(thePackage);return true;}if (thePackage.Name == "References"){appendAndSelectText( thePackage.Notes.ToString(), "Body 1" );createReferencesSection(thePackage);return true;}// use the package notes as body text and indicate to caller that package elements// have not been consumedappendAndSelectText( thePackage.Notes.ToString(), "Body 1" );return false;}private void generatePackageDiagrams( EA.Package thePackage ){// default handling of diagramsforeach(EA.Diagram theDiagram in thePackage.Diagrams){displayProgress( "DIAGRAM: ", theDiagram.Name.ToString() );appendAndSelectDiagramViaClipboard( theDiagram );}}private void generateElementContent( EA.Element theElement, int recurse_level ){recurse_level++;if (recurse_level > 9){throw new System.StackOverflowException("Max Recursion Depth (9) Exceeded" );}trackDocSection(recurse_level);if (EA_Utils.options.elementTypeFoundInEA_DocGen( theElement.Type )){displayProgress( "ELEMENT: ", theElement.Name.ToString() );if ( theElement.Type.StartsWith("Requirement")&& EA_Utils.options.opt_DisplayRequirementsWithStatus == true ){generateRequirementText( theElement );}else{if (theElement.Type.StartsWith("Requirement")&& EA_Utils.options.opt_DisplayRequirementElementsAsSections == false ){string s;s = EA_Utils.options.opt_RequirementElementDisplayFormat;s = s.Replace(@"%s",@"{0}");appendAndSelectText( String.Format( s, theElement.Name ), "Body 1");WordRange.Font.Bold = 1;}else if ( EA_Utils.options.opt_ElementHeadingTransitionLevel > 0&& EA_Utils.options.opt_ElementHeadingTransitionLevel <= recurse_level )appendAndSelectNumParaText( theElement.Name.ToString(), recurse_level );elseappendAndSelectHeadingText( theElement.Name.ToString(), recurse_level );appendAndSelectText( theElement.Notes.ToString(), "Body 1" );}}else{displayProgress( "FILTERED: ", theElement.Type );}// TODO// We will probably have to put special code in here at some point for attributes// of, and collections of things within elements. This will become apparent once// someone tries to produce a design document where such things need to be included// in the generated document. Lots of work needed here eventually.}/// <summary>/// This function was the only way I could figure out how to obtain a style object for/// the style name given. Everything else I tried, didn't work, even code from Visual Basic/// examples failed to work or even compile (once converted) in C#./// </summary>/// <param name="styleText"></param>/// <returns></returns>private Word.Style getStyle(string styleText){foreach(Word.Style aStyle in WordDocument.Styles){//MessageBox.Show( aStyle.NameLocal );if (aStyle.NameLocal.StartsWith(styleText)){return aStyle;}}return null;}/// <summary>/// This function searches for the old content in the document in order to remove it/// prior to adding in the new content from the latest EA model data./// </summary>private void removeExistingDocumentContent(){//WordApp.Visible = true; // enable this when debugging this horrible functionWord.Selection aSelection;object findStyle = getStyle("Heading 1");WordApp.Selection.WholeStory();aSelection = WordApp.Selection;aSelection.Find.ClearFormatting();aSelection.Find.set_Style( ref findStyle );aSelection.Find.Text = "Introduction";aSelection.Find.Replacement.Text = "";aSelection.Find.Forward = true;aSelection.Find.MatchCase = false;aSelection.Find.MatchWholeWord = false;aSelection.Find.MatchWildcards = false;aSelection.Find.MatchSoundsLike = false;aSelection.Find.MatchAllWordForms = false;object missingValue = Type.Missing;if (aSelection.Find.Execute(ref missingValue, ref missingValue,ref missingValue, ref missingValue, ref missingValue,ref missingValue, ref missingValue, ref missingValue,ref missingValue, ref missingValue, ref missingValue,ref missingValue, ref missingValue, ref missingValue,ref missingValue)){// found Introduction with Heading 1 style so we are now at the point in the// document where everything here on must be deleted in readiness for the newly// generated content. Extend the range to the end of the document and cut out the// old content.Word.Range range = aSelection.Range;range.End = WordDocument.Content.End;range.Cut();}}/// <summary>/// Parses an element (because elements can contain sub-elements)./// This is also the function that identifies the occurrences of the special/// EA_DocGenXXX elements./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>private void parse_element(EA.Element theElement, int recurse_level){// First look for and handle special elements that control the doc-gen process// The EA_DocGenPackageLink can contain a list of GUIDs in its notes section// that point to model structure outside of the localised structure forming the// document. This enables a document to grab content from other external models// as long as they are loaded into the repository.if (theElement.Name.StartsWith("EA_DocGenPackageLink")){processPackageLink( theElement, recurse_level );}// The EA_DocGenDiagramLink can contain a list of GUIDs in its notes section// that point to diagram elements anywhere in the repository.else if (theElement.Name.StartsWith("EA_DocGenDiagramLink")){processDiagramLink( theElement );}// The EA_DocGenDiagramLink can contain a list of GUIDs in its notes section// that point to diagram elements anywhere in the repository.else if (theElement.Name.StartsWith("EA_DocGenElementLink")){processElementLink( theElement, recurse_level );}// The EA_DocGenTable element can contain table content in its notes section// and must be dealt with in a special way.else if (theElement.Name.StartsWith("EA_DocGenTable")){processTableElement( theElement, recurse_level );}// The EA_DocGenText element can contain text content in its notes section// that must be appended to the doc in Body 1 style within the current// section.else if (theElement.Name.StartsWith("EA_DocGenText")){processTextElement( theElement, recurse_level );}else if (theElement.Name.StartsWith("EA_DocGenRelationshipMatrix")){processRelationshipMatrixElement( theElement, recurse_level );}else{generateElementContent( theElement, recurse_level );}}/// <summary>/// Parses the package and all of its content, extracting information as needed, and/// adding it to the word document. This method is recursive, since recursion is the/// easiest way to walk thru' the package levels in the model./// </summary>/// <param name="parentPackage"></param>/// <param name="recurse_level"></param>private void parse_package(EA.Package thePackage, int recurse_level){// track recursion level to control heading style formatting requirements// The one-shot skiproot feature of the EA_DocGenPackageLink element handling has an effect// here, as you can see.bool rootPackageWasSkipped = !oneShot_skipRootPackage;if (oneShot_skipRootPackage == false){recurse_level++;trackDocSection(recurse_level);}if (recurse_level > 9){throw new System.StackOverflowException("Max Recursion Depth (9) Exceeded" );}// generate package heading and description. This may, depending on the package name,// consume the elements in the package. If it does not, then consume them (and any diagrams)// locally here.if (false == generatePackageHeadingAndDescription( thePackage, recurse_level )){// consume diagramsgeneratePackageDiagrams( thePackage );// consume elements - we have to use a special sorting class here because of peculiarties// in the way EA holds the elements in the collections.EA_ElementSorter elementSorter = new EA_ElementSorter(thePackage);EA.Element theElement = null;int theElementsRelativeLevel = 0;if (true == elementSorter.getFirst(ref theElement, ref theElementsRelativeLevel)){do{int theElementsRecurseLevel = recurse_level + theElementsRelativeLevel;parse_element( theElement, theElementsRecurseLevel );} while (true == elementSorter.getNext(ref theElement, ref theElementsRelativeLevel));}}// Scan through the packages within this package.foreach(EA.Package lowerLevelPackage in thePackage.Packages){// recurseparse_package(lowerLevelPackage, recurse_level);}if (rootPackageWasSkipped == false)recurse_level--;}/// <summary>/// This is the overall generate document method. It creates a word document from/// an input template, sets various characteristics of the document, schedules the/// EA parsing function, before finally saving the document to the output file name./// </summary>private void createTheWordDoc(){try{object template = this.textBox_template.Text;object newTemplate = Type.Missing;object outputFilename = this.textBox_output_file.Text;object docType = Type.Missing;object visible = Type.Missing;object nothing = Type.Missing;object notTrue = false;EA_Repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME, "", 0);EA_Repository.WriteOutput( Main.GUI_OUTPUT_TAB_NAME,String.Format( "Generating Document {0}", this.textBox_output_file.Text), 0 );// tell word not to show itselfWordApp.Visible = this.checkBox_WordVisibility.Checked;// Create a document from the input templateWordDocument = WordApp.Documents.Add(ref template, ref newTemplate, ref docType, ref visible);// Remove all old content from the input document/template. Note that this assumes// that content will begin at section 1 with the title "Introduction" and it will have// "Heading 1" style.removeExistingDocumentContent();// turn off grammar and spell checkingWordDocument.GrammarChecked = false;WordDocument.SpellingChecked = false;// Parse EA_DocGen Document Model. The parent package is the document model itself// and the packages within it are the top level packages only.// Question: do we have to parse diagrams and elements at this top level? So far,// this has been found to be unecessary, but you never know. For now, dont do it.foreach(EA.Package thePackage in EA_ParentPackage.Packages){parse_package(thePackage, 0);}// TODO// Can we run a word macro from this addin? If so, we should open the doc properties// dialog to let user enter/update the properties.// We could emulate the UpdateAllDocumentFields subroutine written in VB within the// ERG standard template. For now though, do some basic stuff only.// Select the entire document, and Update all fields, including Table of Contents// and then collapse the selection in case user wants to view the document, because// they wont want to see the inverse video of the selection.WordApp.Selection.WholeStory();WordApp.Selection.Fields.Update();object direction = Word.WdCollapseDirection.wdCollapseEnd;WordApp.Selection.Collapse(ref direction);// Save the document to the output fileWordDocument.SaveAs( ref outputFilename,ref nothing, ref nothing, ref nothing, ref nothing,ref nothing, ref nothing, ref nothing, ref nothing,ref nothing, ref nothing, ref nothing, ref nothing,ref nothing, ref nothing, ref nothing);// Tell user the process completed oktextBox_progress.AppendText( "---Completed---" );MessageBox.Show("Document Generation Complete");}catch (Exception createTheWordDoc_exception){MessageBox.Show("Document Generation Failed\n\n" + createTheWordDoc_exception.Message);}}/// <summary>/// Generates the text for requirements. This uses custom styles to format the text/// rather than formatting it in code./// </summary>private void generateRequirementText( EA.Element theElement ){string reqID = "";string reqShortDesc = "";string reqNotes = "";string reqStatusText = "";string reqHeadingStyle ="";string reqParaStyle = "";object reqNameStyle = "Requirements Name";Word.Range shortDescRange;// Set the style depending on the statusswitch ( theElement.Status ){case "Proposed":reqStatusText = "(Proposed)";reqHeadingStyle = "Requirements Proposed Heading";reqParaStyle = "Requirements Proposed Body";break;case "Rejected":reqHeadingStyle = "Requirements Rejected Heading";reqParaStyle = "Requirements Rejected Body";break;case "Approved":reqStatusText = "(Phase " + theElement.Phase + ")";reqHeadingStyle = "Requirements Approved Heading";reqParaStyle = "Requirements Approved Body";break;default:reqStatusText = "(" + theElement.Status + ")(Phase " + theElement.Phase + ")";reqHeadingStyle = "Requirements Approved Heading";reqParaStyle = "Requirements Approved Body";break;}// Pull out the ID from the nameint pos = theElement.Name.IndexOf( " ", 0, theElement.Name.Length );reqID = theElement.Name.Substring( 0, pos );// Pull out the short description from the rest of the namereqShortDesc = theElement.Name.Substring( pos, theElement.Name.Length-pos );reqShortDesc = reqShortDesc.Trim();// Pull out the notesreqNotes = theElement.Notes.ToString();// Add the textappendAndSelectText( reqID + '\t', reqHeadingStyle );appendAndSelectText( reqShortDesc, reqHeadingStyle, true );shortDescRange = WordRange;appendAndSelectText( '\t' + reqStatusText, reqHeadingStyle, true );//reapply the name char style to the short desc.shortDescRange.Start = shortDescRange.Start-1;shortDescRange.End = WordRange.Start-1;shortDescRange.set_Style( ref reqNameStyle );appendAndSelectText( reqNotes, reqParaStyle );}public class elementSortByName : IComparer{int IComparer.Compare( object x, object y){if (x!=null & y!= null)return (new CaseInsensitiveComparer()).Compare( ((EA.Element)x).Name, ((EA.Element)y).Name );elsereturn 0;}}/// <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./// </summary>/// <param name="theElement"></param>/// <param name="recurse_level"></param>private void processRelationshipMatrixElement( EA.Element theElement, int recurse_level ){int tableNum = 0;Word.Table table = null;// process the options for the relationship matrixEA_RelationshipMatrix EA_RelMatrix = new EA_RelationshipMatrix(EA_Utils);if (!EA_RelMatrix.processRelationshipMatrixOptions( theElement )){return; // must have been an error}// Scan the fromPackage to find out how many elements are present and so how many// rows the matrix table will needElementAccumulator fromLister = new ElementAccumulator(EA_RelMatrix.fromElementTypes);EA_Utils.findAndProcessPackageElements( EA_RelMatrix.fromPackage, fromLister, EA_RelMatrix.fromPackageRecursion );// Sort the "from" elementselementSortByName sorter = new elementSortByName();fromLister.Elements.Sort( sorter );// dictionary to support to-from table construction.ArrayList fromToDictionary = new ArrayList();// create the table in the word docif (EA_RelMatrix.fromToTableTitle != null){if (EA_RelMatrix.fromIntroText != null){appendAndSelectText( EA_RelMatrix.fromIntroText, "Body 1" );}tableNum = createTable( EA_RelMatrix.fromToTableTitle, fromLister.Elements.Count + 1, 2 );table = WordDocument.Tables[tableNum];table.Cell(1,1).Range.Text = EA_RelMatrix.fromColumnTitle;table.Cell(1,1).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,1).Range.Font.Bold = 1;table.Cell(1,2).Range.Text = EA_RelMatrix.toColumnTitle;table.Cell(1,2).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,2).Range.Font.Bold = 1;}int row = 2;foreach(EA.Element fromElement in fromLister.Elements){if (EA_RelMatrix.fromToTableTitle != null)table.Cell(row,1).Range.Text = fromElement.Name;// look at the elements connection collection to find references to the// destination elementsEA.Collection conCollection = fromElement.Connectors;bool firstCon = true;foreach (EA.Connector thisCon in conCollection){EA.Element destE = EA_Repository.GetElementByID( thisCon.SupplierID );if (destE == null)continue;// if the destination element is of a type that the user has requested to include...if (!EA_RelMatrix.toElementTypes.Contains( destE.Type ))continue;if ( (destE.Type == "Requirement") && (destE.Status == "Rejected") )continue;// Output the textif (firstCon){firstCon = false;if (EA_RelMatrix.fromToTableTitle != null)table.Cell(row,2).Range.Text = destE.Name;}else{if (EA_RelMatrix.fromToTableTitle != null)table.Cell(row,2).Range.Text += destE.Name;}// 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// for the from-to table.if (EA_RelMatrix.toFromTableTitle != null){DictionaryEntry newEntry = new DictionaryEntry(destE.ElementID, fromElement.Name);fromToDictionary.Add( newEntry );}}row++;}// Does user want a to-from table ?if (EA_RelMatrix.toFromTableTitle != null){ElementAccumulator toLister = new ElementAccumulator(EA_RelMatrix.toElementTypes);EA_Utils.findAndProcessPackageElements( EA_RelMatrix.toPackage, toLister, EA_RelMatrix.toPackageRecursion );// Sort the "to" elementstoLister.Elements.Sort( sorter );if (EA_RelMatrix.toIntroText != null){appendAndSelectText( EA_RelMatrix.toIntroText, "Body 1" );}// create the table in the word doctableNum = createTable( EA_RelMatrix.toFromTableTitle, toLister.Elements.Count + 1, 2 );table = WordDocument.Tables[tableNum];table.Cell(1,1).Range.Text = EA_RelMatrix.toColumnTitle;table.Cell(1,1).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,1).Range.Font.Bold = 1;table.Cell(1,2).Range.Text = EA_RelMatrix.fromColumnTitle;table.Cell(1,2).Range.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;table.Cell(1,2).Range.Font.Bold = 1;// 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.row = 2;foreach(EA.Element fromElement in toLister.Elements){// fromtable.Cell(row,1).Range.Text = fromElement.Name;// skip rejected requirements - not needed because it is done in the ElementAccumulator class//if ( (fromElement.Type == "Requirement") && (fromElement.Status == "Rejected") )// continue;// to'sbool firstCon = true;foreach (DictionaryEntry de in fromToDictionary){if ((int)de.Key == fromElement.ElementID){if (firstCon){firstCon = false;table.Cell(row,2).Range.Text = (string)de.Value;}else{table.Cell(row,2).Range.Text += (string)de.Value;}}}row++;}}}#endregion}}