Subversion Repositories DevTools

Rev

Rev 2128 | Blame | Compare with Previous | Last modification | View Log | RSS feed

using System;
using System.Collections;
using System.Text;
using System.Web;
using System.IO;
using Microsoft.Office.Interop.Word;

namespace EA_DocGen
{
        /// <summary>
        /// Summary description for TextualContent.
        /// </summary>
   public class TextualContent
   {
      private static char[] trim_newlines;

      public static void initialise()
      {
         string s_trim_newlines = "\r\n";
         trim_newlines = s_trim_newlines.ToCharArray();
      }

       public static Range appendDescription(string wordText)
      {
         return appendDescription(wordText, false);
      }

       public static Range appendDescription(string wordText, bool continuation)
      {
         return appendDescription(wordText, EA_Constants.styleName_Body1, continuation);
      }

       public static Range appendDescription(string wordText, string styleName, bool continuation)
      {
          Range wr = null;

         if (wordText != null && wordText.Length > 0)
         {
            wr = appendAndSelectText(wordText, styleName, continuation);
         }
         else if (!EA_DocGenOptions.optionValue(EA_DocGenOptions.boolean_options_e.SUPPRESS_ELEMENT_DESC_MISSING_WARNINGS))
         {
            wr = appendAndSelectText("Description missing!", styleName, continuation);

            if (wr.Characters.Last.Text.Equals("\r"))
               wr.End = wr.End - 1;  // dont italicise the \r char at the end
            wr.Font.Italic = 1;
            wr.Font.Color = WdColor.wdColorRed;
         }
         return wr;
      }



      /// <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>
       public static Range appendAndSelectText(string wordText, string styleText)
      {
         return appendAndSelectText(wordText, styleText, false );
      }


       public static Range appendAndSelectText(string wordText, string styleText, bool continuation)
      {
         if (wordText != null && wordText.Length > 0)
         {
            Range WordRange = null;
            object startLocation;
            object endLocation;

            object style = styleText;
            int i;
            startLocation = 0;
            endLocation = i = createWordDoc.WordDocument.Content.End;

            WordRange = createWordDoc.WordDocument.Range(ref startLocation, ref endLocation);

            if (!continuation)
               WordRange.InsertAfter( "\n" );

            WordRange.InsertAfter( wordText );

            // Make a range out of the pasted text
            startLocation = i;
            endLocation = createWordDoc.WordDocument.Content.End;
            WordRange = createWordDoc.WordDocument.Range(ref startLocation, ref endLocation);

            // and set the pasted text style
            WordRange.set_Style(ref style);

            // compensate for wierd off by one error in the range object when using continuation
            if (continuation)
                WordRange.Start--;

            return WordRange;
         }
         return null;
      }


      /// <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>
      public static void appendAndSelectHeadingText(string wordText, int level)
      {
         // A caller is requesting a new heading level so pass the level to the tracking
         // object so that we can predict exactly what the level numbering should be for
         // diagnostics and fake heading generation (see below in the switch statement).
         DocSectionTracking.trackDocSection(level);

         // Convert level to heading style
         string styleText;
         switch(level)
         {
            case 1: styleText = EA_Constants.styleName_Heading1; break;
            case 2: styleText = EA_Constants.styleName_Heading2; break;
            case 3: styleText = EA_Constants.styleName_Heading3; break;
            case 4: styleText = EA_Constants.styleName_Heading4; break;
            case 5: styleText = EA_Constants.styleName_Heading5; break;
            case 6: styleText = EA_Constants.styleName_Heading6; break;
            case 7: styleText = EA_Constants.styleName_Heading7; break;
            case 8: styleText = EA_Constants.styleName_Heading8; break;
            case 9: styleText = EA_Constants.styleName_Heading9; break;

            default:
               // MS-Word cannot produce headings above level 9 and so we have to
               // fake a heading by constructing it from our document section tracking data.
               styleText = EA_Constants.styleName_Heading10OrAbove;
               wordText = DocSectionTracking.formHeadingString(wordText);
               break;
         }
         // append the text as a heading
         appendAndSelectText(wordText, styleText);
      }

      /// <summary>
      /// Appends a specified text string to the word document, selects the new text, and applies
      /// a numpara level style to it.
      /// </summary>
      /// <param name="wordText"></param>
      /// <param name="level"></param>
      public static void appendAndSelectNumParaText(string wordText, int level)
      {
         // A caller is requesting a new heading level so pass the level to the tracking
         // object so that we can predict exactly what the level numbering should be for
         // diagnostics and fake heading generation (see below in the switch statement).
         DocSectionTracking.trackDocSection(level);

         // Convert level to heading style
         string styleText;
         switch(level)
         {
            case 1: styleText = EA_Constants.styleName_NumPara1; break;
            case 2: styleText = EA_Constants.styleName_NumPara2; break;
            case 3: styleText = EA_Constants.styleName_NumPara3; break;
            case 4: styleText = EA_Constants.styleName_NumPara4; break;
            case 5: styleText = EA_Constants.styleName_NumPara5; break;
            case 6: styleText = EA_Constants.styleName_NumPara6; break;
            case 7: styleText = EA_Constants.styleName_NumPara7; break;
            case 8: styleText = EA_Constants.styleName_NumPara8; break;
            case 9: styleText = EA_Constants.styleName_NumPara9; break;

            default:
               // MS-Word cannot produce headings above level 9 and so we have to
               // fake a heading by constructing it from our document section tracking data.
               styleText = EA_Constants.styleName_NumPara10OrAbove;
               wordText = DocSectionTracking.formHeadingString(wordText);
               break;
         }
         // append the text as a heading
         appendAndSelectText(wordText, styleText);
      }


      public static string trimTrailingReturns(string s)
      {
         if (s != null)
            return s.TrimEnd(trim_newlines);
         else
            return null;
      }



      public static string testSuiteName(EA.Element theElement)
      {
         return "Test Suite - " + theElement.Name;
      }


      public static void appendUnitTestSuite(EA.Element theElement, int recurse_level, ref ArrayList classList)
      {
         if (theElement.StereotypeEx.IndexOf("API") < 0)
         {
            // element does not have an API stereotype, so is not a class for which unit tests are expected.
            return;
         }

         // only feed non-private classes through to the unit test section of a document
         // NOTE: May need an override option for this filter.
         if (!theElement.Visibility.StartsWith("Private"))
         {
            appendAndSelectHeadingText(testSuiteName(theElement), recurse_level+1);

            if (theElement.Tests.Count > 0)
            {
               classList.Add( theElement.ElementID );

               foreach(EA.Test theTest in theElement.Tests)
               {
                  appendAndSelectHeadingText("Test Case - " + theTest.Name, recurse_level+2);

                  if (EA_DocGenOptions.optionValue(EA_DocGenOptions.boolean_options_e.USE_NUM_PARA_FOR_GENERATED_TEST_CASES) == true)
                     appendAndSelectNumParaText("Description", recurse_level+3);
                  else
                     appendAndSelectHeadingText("Description", recurse_level+3);
                  appendAndSelectText(theTest.Notes, EA_Constants.styleName_Body1);

                  if (EA_DocGenOptions.optionValue(EA_DocGenOptions.boolean_options_e.USE_NUM_PARA_FOR_GENERATED_TEST_CASES) == true)
                     appendAndSelectNumParaText("Inputs", recurse_level+3);
                  else
                     appendAndSelectHeadingText("Inputs", recurse_level+3);
                  appendAndSelectText(theTest.Input, EA_Constants.styleName_Body1);

                  if (EA_DocGenOptions.optionValue(EA_DocGenOptions.boolean_options_e.USE_NUM_PARA_FOR_GENERATED_TEST_CASES) == true)
                     appendAndSelectNumParaText("Expected Results", recurse_level+3);
                  else
                     appendAndSelectHeadingText("Expected Results", recurse_level+3);
                  appendAndSelectText(theTest.AcceptanceCriteria, EA_Constants.styleName_Body1);
               }
            }
            else
            {
               Range wr = appendAndSelectText("Test Cases missing!", EA_Constants.styleName_Body1);
               if (wr.Characters.Last.Text.Equals("\r"))
                  wr.End = wr.End - 1;  // dont italicise the \r char at the end
               wr.Font.Italic = 1;
               wr.Font.Color = WdColor.wdColorRed;
            }
         }
      }


      public static void appendUnitTestSuite(EA.Package thePackage, int recurse_level, ref ArrayList classList)
      {
         EA_ElementSorter elementSorter = new EA_ElementSorter(thePackage);
         EA.Element theElement = null;
         int theElementsRelativeLevel = 0;
         if (true == elementSorter.getFirst(ref theElement, ref theElementsRelativeLevel))
         {
            do
            {
               if (theElement.Type.StartsWith("Class"))
               {
                  appendUnitTestSuite(theElement, recurse_level, ref classList);
               }

            } while (true == elementSorter.getNext(ref theElement, ref theElementsRelativeLevel));
         }

         // Scan through the packages within this package.
         foreach(EA.Package lowerLevelPackage in thePackage.Packages)
         {
            // recurse
            appendUnitTestSuite(lowerLevelPackage, recurse_level, ref classList);
         }
      }


      public static void SelectInsertionPointAtEndOfDocument()
      {
         object unit;
         object extend;

         unit = WdUnits.wdStory;
         extend = WdMovementType.wdMove;
         createWordDoc.WordApp.Selection.EndKey(ref unit, ref extend);
      }


      /// <summary>
      /// Generates the text for requirements.
      /// </summary>
      public static bool generateRequirementText( EA.Element theElement )
      {
         if (theElement.Type.StartsWith("Requirement"))
         {
            string reqID = "";
            string reqShortDesc = "";
            string reqHeadingStyle ="";
            string reqParaStyle = "";


            // Set the style depending on the status
            switch ( theElement.Status )
            {
               case "Proposed":
                  reqHeadingStyle = EA_Constants.styleName_ReqPropHdr;
                  reqParaStyle    = EA_Constants.styleName_ReqPropBody;
                  break;

               case "Rejected":
                  reqHeadingStyle = EA_Constants.styleName_ReqRejHdr;
                  reqParaStyle    = EA_Constants.styleName_ReqRejBody;
                  break;

               case "Approved":
                  reqHeadingStyle = EA_Constants.styleName_ReqAppHdr;
                  reqParaStyle    = EA_Constants.styleName_ReqAppBody;
                  break;

               default:
                  reqHeadingStyle = EA_Constants.styleName_ReqAppHdr;
                  reqParaStyle    = EA_Constants.styleName_ReqAppBody;
                  break;
            }

            // A requirement name is assumed to consist of:
            //
            //    <ID> [ShortDescription]
            //
            // Where <ID> is defined as
            //
            //    <TAG>n[subLevel]
            //
            // Where n is a numeric integer and sublevel is defined as
            //
            //    .n[sublevel]
            //
            // Some examples
            //    SPR1  My Requirement
            //    SPR1.1 My Sub-requirement
            //    SPR1.1.1 My Sub-requirements Sub-Requirement
            //    CR1
            //
            // Also, it is assumed that the element notes contains a long description
            // of the requirement.

            // Pull out the ID from the name
            int pos = theElement.Name.IndexOf( " ", 0, theElement.Name.Length );
            if (pos > 0)
            {
               reqID = theElement.Name.Substring( 0, pos );
            }

            // Count '.' chars in the ID in order to support the progressive indentation
            // of lower level requirements in the generated document
            int numDots = 0;
            foreach (char c in reqID)
            {
               if (c == '.')
                  numDots++;
            }

            // Calculate what the left/hanging indent should be based on the standard 2.5 cm plus
            // 0.5cm * number of dots in the ID
            // Do calculation directly in points (72 points per inch) to avoid having to call
            // CentimetersToPoints repeatedly in the word app com object, which I have seen
            // cause unspecified exceptions on rare occasions.
            float indent_pts = (float)70.866;
            if (numDots > 0)
            {
               indent_pts += (float)(14.1732 * numDots);
            }

            // Pull out the short description from the rest of the name. If the short description
            // does not exist, substitute in its place, the element notes.
            bool doneNotes = false;
            reqShortDesc = theElement.Name.Substring( pos, theElement.Name.Length-pos );
            reqShortDesc = reqShortDesc.Trim();
            if (reqShortDesc.Length == 0)
            {
               // If there is no short description, then use the element notes instead
               doneNotes = true;
               if (theElement.Notes != null && theElement.Notes.Length > 0)
               {
                  reqShortDesc = theElement.Notes;
               }
            }

            // Add the ID to the document
            Range wr_id = appendAndSelectText( reqID + '\t', reqHeadingStyle );

            // Add the short description to the document
            Range wr_desc = appendAndSelectText( reqShortDesc, reqHeadingStyle, true );
            if (wr_desc != null)
            {
               // Bold the short description, but only if the element notes have not been used
               // as the short description. Element notes may contain a lot of text and having
               // it all bolded can make the document look a little too busy, and the reader
               // may lose sight of the real document headings which are ofcoarse naturally bolded.

               if (!doneNotes)
               {
                  if (wr_desc.Characters.Last.Text.Equals("\r"))
                     wr_desc.End = wr_desc.End - 1;   // Dont boldise the \r paragraph marker
                  wr_desc.Font.Bold = 1;
               }

               // Indent according to the number of dots in the tag
               if (numDots > 0)
               {
                  wr_desc.ParagraphFormat.LeftIndent      = indent_pts;

                  wr_desc.ParagraphFormat.FirstLineIndent = -indent_pts;
               }
            }

            // Add requirement notes/body, if we have not already consumed them for the
            // short description
            if (!doneNotes && theElement.Notes != null && theElement.Notes.Length > 0 && !EA_DocGenOptions.optionValue(EA_DocGenOptions.boolean_options_e.SUPPRESS_REQUIREMENT_NOTES))
            {
               TextParser.parse(theElement.Notes, theElement.ElementID, reqParaStyle, numDots > 0 ? indent_pts:0, false );
            }
            else
            {
               // If the requirement has no body text, its SpaceAfter will be 3, so we adjust it here to 6
               // just like what the body would have, if it were present.
               if (wr_desc != null)
                  wr_desc.ParagraphFormat.SpaceAfter = 6;
               else if (wr_id != null)
                  wr_id.ParagraphFormat.SpaceAfter = 6;
            }

            // tack on the end a source reference for the requirement, if it contains one
            // as evidenced by tagged values
            string src = EA_Utilities.ReadTag(theElement, "SOURCE");
            if (src.Length > 0)
            {
               StringBuilder sb = new StringBuilder();

               string srcVer = EA_Utilities.ReadTag(theElement, "SOURCE_VERSION");
               sb.Append("[");
               sb.Append(src);

               if (srcVer.Length > 0)
               {
                  sb.Append(" v");
                  sb.Append(srcVer);
               }

               string srcSect = EA_Utilities.ReadTag(theElement, "SOURCE_SECTION");
               if (srcSect.Length > 0)
               {
                  sb.Append(" section ");
                  sb.Append(srcSect);
               }
               sb.Append("]");

               Range wrs_body = appendAndSelectText(sb.ToString(), reqParaStyle);
               if (wrs_body.Characters.Last.Text.Equals("\r"))
                  wrs_body.End = wrs_body.End - 1;  // dont italicise the \r char at the end - doing so causes wierd ms-word exceptions later on
               wrs_body.Font.Italic = 1;
               if (numDots > 0)
               {
                  wrs_body.ParagraphFormat.LeftIndent = indent_pts;
               }
            }

            return true;

         }

         return false;
      }

      public static string HtmlDecode(string sHtml)
      {
          StringWriter writer = new StringWriter();
          HttpUtility.HtmlDecode(sHtml, writer);
          return writer.ToString();
      }

   }
}