/* * OLVExporter - Export the contents of an ObjectListView into various text-based formats * * Author: Phillip Piper * Date: 7 August 2012, 10:35pm * * Change log: * 2012-08-07 JPP Initial code * * Copyright (C) 2012 Phillip Piper * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com. */ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; namespace BrightIdeasSoftware { /// /// An OLVExporter converts a collection of rows from an ObjectListView /// into a variety of textual formats. /// public class OLVExporter { /// /// What format will be used for exporting /// public enum ExportFormat { /// /// Tab separated values, according to http://www.iana.org/assignments/media-types/text/tab-separated-values /// TabSeparated = 1, /// /// Alias for TabSeparated /// TSV = 1, /// /// Comma separated values, according to http://www.ietf.org/rfc/rfc4180.txt /// CSV, /// /// HTML table, according to me /// HTML } #region Life and death /// /// Create an empty exporter /// public OLVExporter() {} /// /// Create an exporter that will export all the rows of the given ObjectListView /// /// public OLVExporter(ObjectListView olv) : this(olv, olv.Objects) {} /// /// Create an exporter that will export all the given rows from the given ObjectListView /// /// /// public OLVExporter(ObjectListView olv, IEnumerable objectsToExport) { if (olv == null) throw new ArgumentNullException("olv"); if (objectsToExport == null) throw new ArgumentNullException("objectsToExport"); this.ListView = olv; this.ModelObjects = ObjectListView.EnumerableToArray(objectsToExport, true); } #endregion #region Properties /// /// Gets or sets whether hidden columns will also be included in the textual /// representation. If this is false (the default), only visible columns will /// be included. /// public bool IncludeHiddenColumns { get { return includeHiddenColumns; } set { includeHiddenColumns = value; } } private bool includeHiddenColumns; /// /// Gets or sets whether column headers will also be included in the text /// and HTML representation. Default is true. /// public bool IncludeColumnHeaders { get { return includeColumnHeaders; } set { includeColumnHeaders = value; } } private bool includeColumnHeaders = true; /// /// Gets the ObjectListView that is being used as the source of the data /// to be exported /// public ObjectListView ListView { get { return objectListView; } set { objectListView = value; } } private ObjectListView objectListView; /// /// Gets the model objects that are to be placed in the data object /// public IList ModelObjects { get { return modelObjects; } set { modelObjects = value; } } private IList modelObjects = new ArrayList(); #endregion #region Commands /// /// Export the nominated rows from the nominated ObjectListView. /// Returns the result in the expected format. /// /// /// /// This will perform only one conversion, even if called multiple times with different formats. public string ExportTo(ExportFormat format) { if (results == null) this.Convert(); return results[format]; } /// /// Convert /// public void Convert() { IList columns = this.IncludeHiddenColumns ? this.ListView.AllColumns : this.ListView.ColumnsInDisplayOrder; StringBuilder sbText = new StringBuilder(); StringBuilder sbCsv = new StringBuilder(); StringBuilder sbHtml = new StringBuilder(""); // Include column headers if (this.IncludeColumnHeaders) { List strings = new List(); foreach (OLVColumn col in columns) strings.Add(col.Text); WriteOneRow(sbText, strings, "", "\t", "", null); WriteOneRow(sbHtml, strings, "", HtmlEncode); WriteOneRow(sbCsv, strings, "", ",", "", CsvEncode); } foreach (object modelObject in this.ModelObjects) { List strings = new List(); foreach (OLVColumn col in columns) strings.Add(col.GetStringValue(modelObject)); WriteOneRow(sbText, strings, "", "\t", "", null); WriteOneRow(sbHtml, strings, "", HtmlEncode); WriteOneRow(sbCsv, strings, "", ",", "", CsvEncode); } sbHtml.AppendLine("
", "", "
", "", "
"); results = new Dictionary(); results[ExportFormat.TabSeparated] = sbText.ToString(); results[ExportFormat.CSV] = sbCsv.ToString(); results[ExportFormat.HTML] = sbHtml.ToString(); } private delegate string StringToString(string str); private void WriteOneRow(StringBuilder sb, IEnumerable strings, string startRow, string betweenCells, string endRow, StringToString encoder) { sb.Append(startRow); bool first = true; foreach (string s in strings) { if (!first) sb.Append(betweenCells); sb.Append(encoder == null ? s : encoder(s)); first = false; } sb.AppendLine(endRow); } private Dictionary results; #endregion #region Encoding /// /// Encode a string such that it can be used as a value in a CSV file. /// This basically means replacing any quote mark with two quote marks, /// and enclosing the whole string in quotes. /// /// /// private static string CsvEncode(string text) { if (text == null) return null; const string DOUBLEQUOTE = @""""; // one double quote const string TWODOUBEQUOTES = @""""""; // two double quotes StringBuilder sb = new StringBuilder(DOUBLEQUOTE); sb.Append(text.Replace(DOUBLEQUOTE, TWODOUBEQUOTES)); sb.Append(DOUBLEQUOTE); return sb.ToString(); } /// /// HTML-encodes a string and returns the encoded string. /// /// The text string to encode. /// The HTML-encoded text. /// Taken from http://www.west-wind.com/weblog/posts/2009/Feb/05/Html-and-Uri-String-Encoding-without-SystemWeb private static string HtmlEncode(string text) { if (text == null) return null; StringBuilder sb = new StringBuilder(text.Length); int len = text.Length; for (int i = 0; i < len; i++) { switch (text[i]) { case '<': sb.Append("<"); break; case '>': sb.Append(">"); break; case '"': sb.Append("""); break; case '&': sb.Append("&"); break; default: if (text[i] > 159) { // decimal numeric entity sb.Append("&#"); sb.Append(((int)text[i]).ToString(CultureInfo.InvariantCulture)); sb.Append(";"); } else sb.Append(text[i]); break; } } return sb.ToString(); } #endregion } }