/* Formatter.cs * part of zaaReloaded2 * * Copyright 2015-2017 Daniel Kraus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using Microsoft.Office.Interop.Word; using zaaReloaded2.LabModel; using zaaReloaded2.Controller; using zaaReloaded2.Controller.Elements; namespace zaaReloaded2.Formatter { /// /// Formats and writes a to a Word document. /// public class Formatter { #region Properties /// /// Gets or sets the Settings that this Formatter works with. /// public Settings Settings { get; set; } /// /// Gets or sets the that shall be /// formatted. /// public Laboratory Laboratory { [DebuggerStepThrough] get { return _laboratory; } set { _laboratory = value; _timePointFormatters = new TimePointFormatterDictionary(); foreach (TimePoint tp in _laboratory.TimePoints.Values) { _timePointFormatters[tp.TimeStamp] = new TimePointFormatter(tp, Settings.ReferenceStyle, Settings.AbnormalStyle); } } } /// /// Gets the current working set of TimePointFormatters. /// public Dictionary WorkingTimePoints { get; private set; } /// /// Is true if this Formatter object carries a Laboratory with /// at least one time point, and if there are Settings to work with. /// public bool CanRun { get { return (Settings != null) && (Laboratory != null) && (Laboratory.TimePoints.Count > 0); } } #endregion #region Constructors public Formatter() { Settings = new Settings(); _secondaryBuffer = new DocumentWriter(); _primaryBuffer = new DocumentWriter(_secondaryBuffer); } public Formatter(Document document) : this() { Document = document; _secondaryBuffer.Document = document; } #endregion #region Public methods /// /// Writes some text to the current document. /// Does nothing if there is no current document. /// /// Text to write to the current document. /// public void Write(string text) { _primaryBuffer.Write(text); } /// /// Writes a paragraph to the document. /// /// public void WriteParagraph(string text) { _primaryBuffer.WriteLine(text); } /// /// Formats the laboratory and writes it to a Word document. /// /// Word document to write to (at the /// current position of the cursor). public void Run() { if (!CanRun) { if (Settings == null) throw new InvalidOperationException("No settings data to work with."); if ((Laboratory == null) || Laboratory.TimePoints.Count == 0) throw new NoLaboratoryDataException("No laboratory data to format."); throw new InvalidOperationException("Cannot run formatter."); } // Create undo record and styles prior to iterating over the elements // because a column switching element might trigger output to the // document. Helpers.StartUndo("Laborformatierung"); CreateStyles(); int current = 0; while (current < Settings.Elements.Count) { // If there are FormatElements in the first level of the // elements list, collect all consecutive ones and process // them for each individual time point. if (Settings.Elements[current] is FormatElementBase) { int notAFormatElement = CollectFormatElements(current); IList list = Settings.Elements .Skip(current) .Take(notAFormatElement - current) .Cast().ToList(); ProcessAllTimePoints(list); current = notAFormatElement; } else { // The current element is not derived from FormatElementBase; // so go ahead and 'run' it. Settings.Elements[current].Run(this); current++; } } _secondaryBuffer.Flush(); Helpers.EndUndo(); } /// /// Selects one time point per day in the laboratory. /// public void ProcessEachDay(ControlElementBase controlElement) { IEnumerable days = _timePointFormatters.Keys.Select(k => k.Date).Distinct(); foreach (DateTime day in days) { ProcessDay( controlElement, _timePointFormatters .Where(kv => kv.Key.Date == day.Date) .ToDictionary(kv => kv.Key, kv => kv.Value) ); } } /// /// Selects all time points for the first day in the /// laboratory. /// public void ProcessFirstDay(ControlElementBase controlElement) { DateTime first = _timePointFormatters.First().Key; ProcessDay( controlElement, _timePointFormatters .Where(kv => kv.Key.Date == first.Date) .ToDictionary(kv => kv.Key, kv => kv.Value) ); } /// /// Selects all time points for the first day in the /// laboratory. /// public void ProcessLastDay(ControlElementBase controlElement) { DateTime last = _timePointFormatters.Last().Key; ProcessDay( controlElement, _timePointFormatters .Where(kv => kv.Key.Date == last.Date) .ToDictionary(kv => kv.Key, kv => kv.Value) ); } /// /// Processes the FormatElementBase children of /// for each individual time point. /// /// ControlElementBase descendant whose /// FormatElementBase children to process. public void ProcessAllTimePoints(ControlElementBase controlElement) { ProcessAllTimePoints(controlElement.Children); } /// /// Inserts a table with two columns into the document. /// public void InsertTwoColumns() { _secondaryBuffer.Flush(); if (Document != null) { Range r = Document.ActiveWindow.Selection.Range; _table = Document.Tables.Add(r, NumRows: 1, NumColumns: 2); _table.AllowAutoFit = true; _table.AutoFitBehavior(WdAutoFitBehavior.wdAutoFitWindow); _table.PreferredWidthType = WdPreferredWidthType.wdPreferredWidthPercent; _table.PreferredWidth = 100; _table.Borders.Enable = 0; } } /// /// Moves the insertion point to the next column in a layout /// table. /// public void NextColumn() { if (_table == null) { throw new InvalidOperationException( "Kann nicht zur nächsten Spalte wechseln, da bislang keine Tabelle eingefügt wurde."); } _secondaryBuffer.Flush(); Document.ActiveWindow.Selection.MoveRight(WdUnits.wdCell); } /// /// Creates a paragraph and character styles in the document. /// public void CreateStyles() { if (Document != null) { Logger.Info("CreateStyles"); Style style; // Don't see a better way to check for the existence of a particular // paragraph style than by using a try...catch construction. try { style = Document.Styles[Properties.Settings.Default.StyleParagraph]; Logger.Info("CreateStyles: Found paragraph style in document"); } catch { Logger.Info("CreateStyles: Need to create paragraph style"); // Add default paragraph style for laboratory style = Document.Styles.Add(Properties.Settings.Default.StyleParagraph); style.Font.Size = 10; // pt style.Font.Bold = 0; style.Font.Italic = 0; style.Font.Underline = 0; style.ParagraphFormat.SpaceAfter = 0; style.ParagraphFormat.SpaceBefore = 0; style.ParagraphFormat.LeftIndent = 36; // pt style.ParagraphFormat.FirstLineIndent = -36; // pt style.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphJustify; } Bovender.ComHelpers.ReleaseComObject(style); try { style = Document.Styles[Properties.Settings.Default.StyleHeader]; Logger.Info("CreateStyles: Found header style in document"); } catch { Logger.Info("CreateStyles: Need to create header style"); // Add header paragraph style for laboratory style = Document.Styles.Add(Properties.Settings.Default.StyleHeader); style.Font.Size = 10; // pt style.Font.Bold = 1; style.Font.Italic = 0; style.Font.Underline = WdUnderline.wdUnderlineSingle; style.ParagraphFormat.SpaceAfter = 0; style.ParagraphFormat.SpaceBefore = 12; style.ParagraphFormat.LeftIndent = 36; // pt style.ParagraphFormat.FirstLineIndent = -36; // pt style.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphJustify; style.set_NextParagraphStyle(Document.Styles[Properties.Settings.Default.StyleParagraph]); } Bovender.ComHelpers.ReleaseComObject(style); /* try { style = Document.Styles[Properties.Settings.Default.StyleAbnormal]; } catch { // Add character style for abnormal parameters style = Document.Styles.Add( Properties.Settings.Default.StyleAbnormal, WdStyleType.wdStyleTypeCharacter); style.Font.Bold = 1; } */ } } #endregion #region Protected methods /// /// Collects all consecutive FormatElements from Settings.Elements. /// /// Index of the first element that is not a FormatElement. /// protected int CollectFormatElements(int startIndex) { int i = startIndex; while (i < Settings.Elements.Count) { if (!(Settings.Elements[i] is FormatElementBase)) { break; } i++; } return i; } protected void ProcessElements(IList formatElements) { if (formatElements != null) { foreach (ElementBase element in formatElements) { element.Run(this); } } } protected void ProcessAllTimePoints(IList formatElements) { for (int i = 0; i < _timePointFormatters.Count; i++) { WorkingTimePoints = _timePointFormatters .Skip(i) .Take(1) .ToDictionary(kv => kv.Key, kv => kv.Value); ProcessElements(formatElements); if (_primaryBuffer.HasBufferedText) { _primaryBuffer.Prepend( WorkingTimePoints.First().Value.GetDateAndTimeHeader() ); } _primaryBuffer.Flush(); } } protected void ProcessDay( ControlElementBase controlElement, Dictionary workingTimePoints) { if (workingTimePoints == null) throw new ArgumentNullException("workingTimePoints"); WorkingTimePoints = workingTimePoints; ProcessElements(controlElement.Children); if (_primaryBuffer.HasBufferedText) { _primaryBuffer.Prepend( WorkingTimePoints.First().Value.GetDateHeader()); } _primaryBuffer.Flush(); } #endregion #region Protected properties /// /// Gets the working Word document. /// protected Document Document { get; set; } #endregion #region Fields TimePointFormatterDictionary _timePointFormatters; Laboratory _laboratory; DocumentWriter _primaryBuffer; DocumentWriter _secondaryBuffer; Table _table; #endregion #region Class logger private static NLog.Logger Logger { get { return _logger.Value; } } private static readonly Lazy _logger = new Lazy(() => NLog.LogManager.GetCurrentClassLogger()); #endregion } }