Commits

Darius Damalakas committed eb9b06f

Fixing Issue #17 Csv Export
* NewLines will be preserved
* Quotes, commas, and new-line symbols will be escaped

Comments (0)

Files changed (5)

SourceGrid.Examples/GridSamples/GenericSamples/frmSample01.cs

         {
             try
             {
-                string l_Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "CsvFile.csv");
+                var exportFileName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "CsvFile.csv");
 
-                using (System.IO.StreamWriter writer = new System.IO.StreamWriter(l_Path, false, System.Text.Encoding.Default))
+                using (var writer = new System.IO.StreamWriter(exportFileName, false, System.Text.Encoding.Default))
                 {
-                    SourceGrid.Exporter.CSV csv = new SourceGrid.Exporter.CSV();
+                    var csv = new SourceGrid.Exporter.CSV();
                     csv.Export(grid1, writer);
                     writer.Close();
                 }
 
-                DevAge.Shell.Utilities.OpenFile(l_Path);
+                DevAge.Shell.Utilities.OpenFile(exportFileName);
             }
             catch (Exception err)
             {

SourceGrid.Examples/GridSamples/SampleData.xml

 <data>
-  <row ID="1" CustomerId="ALFKI" CompanyName="Alfreds Futterkiste" ContactName="Maria Anders" ContactTitle="Sales Representative" Address="Obere Str. 57" City="Berlin" PostalCode="12209" Country="Germany" Phone="030-0074321" Birthday="01/01/2000 00:00:00" Selected="True" website="http://www.codeproject.com" Price="0" />
+  <row ID="1" CustomerId="ALFKI" CompanyName="Alfreds Futterkiste" ContactName="Maria Anders" ContactTitle="Sales Representative" Address="Obere 
+Street 57" City="Berlin" PostalCode="12209" Country="Germany" Phone="030-0074321" Birthday="01/01/2000 00:00:00" Selected="True" website="http://www.codeproject.com" Price="0" />
   <row ID="2" CustomerId="ANATR" CompanyName="Ana Trujillo Emparedados y helados" ContactName="Ana Trujillo" ContactTitle="Owner" Address="Avda. de la Constitucion 2222" City="Mexico D.F." PostalCode="5021" Country="Mexico" Phone="(5) 555-4729" Birthday="01/01/2002 00:00:00" Selected="True" website="http://www.google.com/" Price="0.5" />
   <row ID="3" CustomerId="ANTON" CompanyName="Antonio Moreno Taqueria" ContactName="Antonio Moreno" ContactTitle="Owner" Address="Mataderos  2312" City="Mexico D.F." PostalCode="5023" Country="Mexico" Phone="(5) 555-3932" Birthday="01/01/2004 00:00:00" Selected="True" website="http://www.go-mono.com/" Price="1" />
   <row ID="4" CustomerId="AROUT" CompanyName="Around the Horn" ContactName="Thomas Hardy" ContactTitle="Sales Representative" Address="120 Hanover Sq." City="London" PostalCode="WA1 1DP" Country="UK" Phone="(171) 555-7788" Birthday="01/01/2006 00:00:00" Selected="True" website="http://www.google.com/" Price="1.5" />

SourceGrid.Tests/Exporter/CsvExporterTest.cs

+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using SourceGrid.Cells;
+using SourceGrid.Exporter;
+
+namespace SourceGrid.Tests.Exporter
+{
+    [TestFixture]
+    class CsvExporterTest
+    {
+        private CsvExporter underTest;
+
+        [SetUp]
+        public void SetUp()
+        {
+            underTest = new CsvExporter();
+        }
+
+        [Test]
+        public void Should_Return_Empty_Given_Empty_Grid()
+        {
+            // Given
+            var grid = NewEmptyGrid();
+            var stream = new StringWriter();
+
+            // When
+            underTest.Export(grid, stream);
+
+            // Then
+            Assert.AreEqual(string.Empty, stream.ToString());
+        }
+
+        [Test]
+        public void Should_NotEnd_With_NewLine_Given_Cell()
+        {
+            // Given
+            const string cellValue = @"Text";
+            var grid = SingeCellGrid(1, 1, cellValue);
+            var stream = new StringWriter();
+
+            // When
+            underTest.Export(grid, stream);
+
+            // Then
+            Assert.AreEqual(cellValue, stream.ToString());
+        }
+
+        [Test]
+        public void Should_Escape_Given_Cell_With_NewLines()
+        {
+            // Given
+            var grid = SingeCellGrid(1, 1, "Multi \r\n line text");
+            var stream = new StringWriter();
+
+            // When
+            underTest.Export(grid, stream);
+
+            // Then
+            Assert.AreEqual("\"Multi \r\n line text\"", stream.ToString());
+        }
+
+        [Test]
+        public void Should_Escape_Given_Cell_With_Comma()
+        {
+            // Given
+            var grid = SingeCellGrid(1, 1, "Text , comma");
+            var stream = new StringWriter();
+
+            // When
+            underTest.Export(grid, stream);
+
+            // Then
+            Assert.AreEqual("\"Text , comma\"", stream.ToString());
+        }
+
+        [Test]
+        public void Should_Escape_Given_Cell_With_QuotationMarks()
+        {
+            // Given
+            var grid = SingeCellGrid(1, 1, "Text \" quote \" ");
+            var stream = new StringWriter();
+
+            // When
+            underTest.Export(grid, stream);
+
+            // Then
+            Assert.AreEqual("\"Text \"\" quote \"\" \"", stream.ToString());
+        }
+
+        private static Grid SingeCellGrid(int rowCount, int columnCount, string cellValue)
+        {
+            var grid = NewGrid(rowCount, columnCount);
+            grid[0, 0] = new SourceGrid.Cells.Cell(cellValue);
+            return grid;
+        }
+
+        private static Grid NewGrid(int rowCount, int columnCount)
+        {
+            var grid = new Grid {ColumnsCount = columnCount};
+            grid.Rows.InsertRange(0, rowCount);
+            return grid;
+        }
+
+        private static GridVirtual NewEmptyGrid()
+        {
+            return new Grid();
+        }
+    }
+}

SourceGrid.Tests/SourceGrid.Tests.csproj

     <Compile Include="Cells\Models\TestValueModel.cs" />
     <Compile Include="Cells\Models\TestValueModel_ValueChange_Events.cs" />
     <Compile Include="ClipBoard\TestClipboard.cs" />
+    <Compile Include="Exporter\CsvExporterTest.cs" />
     <Compile Include="Extensions\PingGrids\TestGrid_Span.cs" />
     <Compile Include="net_2_0\CustomClasses.cs" />
     <Compile Include="net_2_0\TestOverride_With_New.cs" />

SourceGrid/SourceGrid/Exporter/CSV.cs

 using System;
+using System.Diagnostics;
 
 namespace SourceGrid.Exporter
 {
+    [Obsolete("Please use CsvExporter class instead. This will be removed in future releases")]
+    public class CSV : CsvExporter
+    {
+    }
+
     /// <summary>
     /// An utility class to export a grid to a csv delimited format file.
     /// </summary>
-    public class CSV
+    public class CsvExporter
     {
-        public CSV()
+        private const string QUOTE = "\"";
+        private const string ESCAPED_QUOTE = "\"\"";
+        private static char[] CHARACTERS_THAT_MUST_BE_QUOTED = { ',', '"', '\n' };
+
+        public CsvExporter()
         {
             mFieldSeparator = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator;
             mLineSeparator = System.Environment.NewLine;
 
         public virtual void Export(GridVirtual grid, System.IO.TextWriter stream)
         {
-            for (int r = 0; r < grid.Rows.Count; r++)
+            for (var r = 0; r < grid.Rows.Count; r++)
             {
-                for (int c = 0; c < grid.Columns.Count; c++)
+                for (var c = 0; c < grid.Columns.Count; c++)
                 {
                     if (c > 0)
                         stream.Write(mFieldSeparator);
 
-                    Cells.ICellVirtual cell = grid.GetCell(r, c);
-                    Position pos = new Position(r, c);
-                    CellContext context = new CellContext(grid, pos, cell);
-                    ExportCSVCell(context, stream);
+                    var cell = grid.GetCell(r, c);
+                    var pos = new Position(r, c);
+                    var context = new CellContext(grid, pos, cell);
+                    ExportCsvCell(context, stream);
                 }
-                stream.Write(mLineSeparator);
+                if (IsLastLine(grid, r) == false)
+                    stream.Write(mLineSeparator);
             }
         }
 
-        protected virtual void ExportCSVCell(CellContext context, System.IO.TextWriter stream)
+        private bool IsLastLine(GridVirtual grid, int i)
         {
+            return grid.Rows.Count -1 == i;
+        }
+
+        private void ExportCsvCell(CellContext context, System.IO.TextWriter stream)
+        {
+            var text = string.Empty;
             if (context.Cell != null)
-            {
-                string text = context.DisplayText;
-                if (text == null)
-                    text = string.Empty;
-                text = text.Replace("\r\n", " ");
-                text = text.Replace("\n", " ");
-                text = text.Replace("\r", " ");
-                stream.Write(text);
-            }
-            else
-            {
-                stream.Write("");
-            }
+                text = context.DisplayText ?? string.Empty;
+            if (text.Contains(QUOTE))
+                text = text.Replace(QUOTE, ESCAPED_QUOTE);
+            if (text.IndexOfAny(CHARACTERS_THAT_MUST_BE_QUOTED) > -1)
+                text = QUOTE + text + QUOTE;
+            stream.Write(text);
         }
     }