Skip to content

Commit

Permalink
Add deep copy methods to all relevant classes
Browse files Browse the repository at this point in the history
Also finalized ///-comments.
  • Loading branch information
omaus committed Mar 22, 2023
1 parent af5a98b commit 2069ed9
Show file tree
Hide file tree
Showing 12 changed files with 607 additions and 118 deletions.
65 changes: 57 additions & 8 deletions src/FsSpreadsheet.ExcelIO/FsExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ open FsSpreadsheet
open FsSpreadsheet.ExcelIO
open System.IO

/// <summary>
/// Classes that extend the core FsSpreadsheet library with IO functionalities.
/// </summary>
[<AutoOpen>]
module FsExtensions =


type DataType with

/// <summary>Converts a given CellValues to the respective DataType.</summary>
/// <summary>
/// Converts a given CellValues to the respective DataType.
/// </summary>
static member ofXlsxCellValues (cellValues : CellValues) =
match cellValues with
| CellValues.Number -> DataType.Number
Expand All @@ -32,7 +36,9 @@ module FsExtensions =
// let row,col = xlsxCell.CellReference.Value |> CellReference.toIndices
// FsCell.create (int row) (int col) v

/// <summary>Creates an FsCell on the basis of an XlsxCell. Uses a SharedStringTable if present to get the XlsxCell's value.</summary>
/// <summary>
/// Creates an FsCell on the basis of an XlsxCell. Uses a SharedStringTable if present to get the XlsxCell's value.
/// </summary>
static member ofXlsxCell (sst : SharedStringTable option) (xlsxCell : Cell) =
let v = Cell.getValue sst xlsxCell
let col, row = xlsxCell.CellReference.Value |> CellReference.toIndices
Expand All @@ -44,7 +50,9 @@ module FsExtensions =

type FsTable with

/// <summary>
/// Returns the FsTable with given FsCellsCollection in the form of an XlsxTable.
/// </summary>
member self.ToXlsxTable(cells : FsCellsCollection) =

let columns =
Expand All @@ -54,21 +62,26 @@ module FsExtensions =
)
Table.create self.Name (StringValue(self.RangeAddress.Range)) columns

/// <summary>
/// Returns an FsTable with given FsCellsCollection in the form of an XlsxTable.
/// </summary>
static member toXlsxTable cellsCollection (table : FsTable) =
table.ToXlsxTable(cellsCollection)

///// Creates an FsTable on the basis of an XlsxTable.
//new(table : Spreadsheet.Table) = // not permitted :(
//FsTable(table)

/// <summary>
/// Takes an XlsxTable and returns an FsTable.
static member fromXlsxTable table =
let topLeftBoundary, bottomRightBoundary = Table.getArea table |> Table.Area.toBoundaries
let ra = FsRangeAddress(FsAddress(topLeftBoundary), FsAddress(bottomRightBoundary))
FsTable(table.Name, ra, table.TotalsRowShown, true)

/// <summary>Returns the FsWorksheet associated with the FsTable in a given FsWorkbook.</summary>
/// <summary>
/// Returns the FsWorksheet associated with the FsTable in a given FsWorkbook.
/// </summary>
member self.GetWorksheetOfTable(workbook : FsWorkbook) =
workbook.GetWorksheets()
|> List.find (
Expand All @@ -77,14 +90,18 @@ module FsExtensions =
|> List.exists (fun t -> t.Name = self.Name)
)

/// <summary>Returns the FsWorksheet associated with a given FsTable in an FsWorkbook.</summary>
/// <summary>
/// Returns the FsWorksheet associated with a given FsTable in an FsWorkbook.
/// </summary>
static member getWorksheetOfTable workbook (table : FsTable) =
table.GetWorksheetOfTable workbook


type FsWorksheet with

/// <summary>
/// Returns the FsWorksheet in the form of an XlsxSpreadsheet.
/// </summary>
member self.ToXlsxWorksheet() =
self.RescanRows()
let sheet = Worksheet.empty()
Expand All @@ -110,26 +127,34 @@ module FsExtensions =
sd
Worksheet.setSheetData sheetData sheet

/// <summary>
/// Returns an FsWorksheet in the form of an XlsxSpreadsheet.
/// </summary>
static member toXlsxWorksheet (fsWorksheet : FsWorksheet) =
fsWorksheet.ToXlsxWorksheet()

/// <summary>
/// Appends the FsTables of this FsWorksheet to a given OpenXmlWorksheetPart in an XlsxWorkbookPart.
/// </summary>
member self.AppendTablesToWorksheetPart(xlsxlWorkbookPart : DocumentFormat.OpenXml.Packaging.WorkbookPart, xlsxWorksheetPart : DocumentFormat.OpenXml.Packaging.WorksheetPart) =
self.Tables
|> Seq.iter (fun t ->
let table = t.ToXlsxTable(self.CellCollection)
Table.addTable xlsxlWorkbookPart xlsxWorksheetPart table |> ignore
)

/// <summary>
/// Appends the FsTables of an FsWorksheet to a given OpenXmlWorksheetPart in an XlsxWorkbookPart.
/// </summary>
static member appendTablesToWorksheetPart xlsxWorkbookPart xlsxWorksheetPart (fsWorksheet : FsWorksheet) =
fsWorksheet.AppendTablesToWorksheetPart(xlsxWorkbookPart, xlsxWorksheetPart)


type FsWorkbook with

/// <summary>Creates an FsWorkbook from a given Stream to an XlsxFile.</summary>
/// <summary>
/// Creates an FsWorkbook from a given Stream to an XlsxFile.
/// </summary>
// TO DO: Ask HLW/TM: is this REALLY the way to go? This is not a constructor! (though it tries to be one)
member self.FromXlsxStream (stream : Stream) =
let doc = Spreadsheet.fromStream stream false
Expand Down Expand Up @@ -175,18 +200,24 @@ module FsExtensions =
sheets
|> Seq.fold (fun wb sheet -> FsWorkbook.addWorksheet sheet wb) (new FsWorkbook())

/// <summary>Creates an FsWorkbook from a given Stream to an XlsxFile.</summary>
/// <summary>
/// Creates an FsWorkbook from a given Stream to an XlsxFile.
/// </summary>
static member fromXlsxStream (stream : Stream) =
(new FsWorkbook()).FromXlsxStream stream

/// <summary>Takes the path to an Xlsx file and returns the FsWorkbook based on its content.</summary>
/// <summary>
/// Takes the path to an Xlsx file and returns the FsWorkbook based on its content.
/// </summary>
static member fromXlsxFile (filePath : string) =
let sr = new StreamReader(filePath)
let wb = FsWorkbook.fromXlsxStream sr.BaseStream
sr.Close()
wb

/// <summary>
/// Writes the FsWorkbook into a given MemoryStream.
/// </summary>
member self.ToStream(stream : MemoryStream) =
let doc = Spreadsheet.initEmptyOnStream stream

Expand All @@ -206,40 +237,58 @@ module FsExtensions =

Spreadsheet.close doc

/// <summary>
/// Writes an FsWorkbook into a given MemoryStream.
/// </summary>
static member toStream stream (workbook : FsWorkbook) =
workbook.ToStream stream

/// <summary>
/// Returns the FsWorkbook in the form of a byte array.
/// </summary>
member self.ToBytes() =
use memoryStream = new MemoryStream()
self.ToStream(memoryStream)
memoryStream.ToArray()

/// <summary>
/// Returns an FsWorkbook in the form of a byte array.
/// </summary>
static member toBytes (workbook: FsWorkbook) =
workbook.ToBytes()

/// <summary>
/// Writes the FsWorkbook into a binary file at the given path.
/// </summary>
member self.ToFile(path) =
self.ToBytes()
|> fun bytes -> File.WriteAllBytes (path, bytes)

/// <summary>
/// Writes an FsWorkbook into a binary file at the given path.
/// </summary>
static member toFile path (workbook : FsWorkbook) =
workbook.ToFile(path)


type Writer =


/// <summary>
/// Writes an FsWorkbook into a given MemoryStream.
/// </summary>
static member toStream(stream : MemoryStream, workbook : FsWorkbook) =
workbook.ToStream(stream)


/// <summary>
/// Returns an FsWorkbook in the form of a byte array.
/// </summary>
static member toBytes(workbook: FsWorkbook) =
workbook.ToBytes()


/// <summary>
/// Writes an FsWorkbook into a binary file at the given path.
/// </summary>
static member toFile(path,workbook: FsWorkbook) =
workbook.ToFile(path)
55 changes: 47 additions & 8 deletions src/FsSpreadsheet/Cells/FsCell.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

open System

/// Possible DataTypes used in a FsCell
/// <summary>
/// Possible DataTypes used in a FsCell.
/// </summary>
type DataType =
| String
| Boolean
| Number
| Date
| Empty

/// <summary>
/// Returns the proper CellValues case for the given value.
/// </summary>
static member InferCellValue (value : 'T) =
let value = box value
match value with
Expand All @@ -32,7 +36,9 @@ type DataType =
| _ -> DataType.String,value.ToString()

// Type based on the type XLCell used in ClosedXml
/// <summary>
/// Creates an FsCell of `DataType` dataType, with value of type `string`, and `FsAddress` address.
/// </summary>
type FsCell (value : IConvertible, dataType : DataType, address : FsAddress) =

// TODO: Maybe save as IConvertible
Expand Down Expand Up @@ -99,17 +105,23 @@ type FsCell (value : IConvertible, dataType : DataType, address : FsAddress) =
with get() = _dataType
and internal set(dataType) = _dataType <- dataType

/// <summary>
/// Gets or sets the columnIndex of the FsCell.
/// </summary>
member self.ColumnNumber
with get() = _columnIndex
and set(colI) = _columnIndex <- colI

/// <summary>
/// Gets or sets the rowIndex of the FsCell.
/// </summary>
member self.RowNumber
with get() = _rowIndex
and set(rowI) = _rowIndex <- rowI

/// <summary>Gets this FsCell's address, relative to the FsWorksheet.</summary>
/// <summary>
/// Gets this FsCell's address, relative to the FsWorksheet.
/// </summary>
/// <value>The FsCell's address.</value>
member self.Address
with get() = FsAddress(_rowIndex,_columnIndex)
Expand All @@ -118,25 +130,35 @@ type FsCell (value : IConvertible, dataType : DataType, address : FsAddress) =
_columnIndex <- address.ColumnNumber


/// <summary>Create an FsCell from given rowNumber, colNumber, and value. Infers the DataType.</summary>
/// <summary>
/// Create an FsCell from given rowNumber, colNumber, and value. Infers the DataType.
/// </summary>
static member create (rowNumber : int) (colNumber : int) value =
let dataT, value = DataType.InferCellValue value
FsCell(value, dataT, FsAddress(rowNumber, colNumber))

/// <summary>Creates an empty FsCell.</summary>
/// <summary>
/// Creates an empty FsCell.
/// </summary>
static member createEmpty () =
FsCell("", DataType.Empty, FsAddress(0,0))

/// <summary>Creates an FsCell with the given FsAdress and value. Inferes the DataType.</summary>
/// <summary>
/// Creates an FsCell with the given FsAdress and value. Inferes the DataType.
/// </summary>
static member createWithAdress (adress : FsAddress) value =
let dataT, value = DataType.InferCellValue value
FsCell(value, dataT, adress)

/// <summary>Creates an empty FsCell with a given FsAddress.</summary>
/// <summary>
/// Creates an empty FsCell with a given FsAddress.
/// </summary>
static member createEmptyWithAdress (adress : FsAddress) =
FsCell("", DataType.Empty, adress)

/// <summary>Creates an FsCell with the given DataType, rowNumber, colNumber, and value.</summary>
/// <summary>
/// Creates an FsCell with the given DataType, rowNumber, colNumber, and value.
/// </summary>
static member createWithDataType (dataType : DataType) (rowNumber : int) (colNumber : int) value =
FsCell(value, dataType, FsAddress(rowNumber, colNumber))

Expand Down Expand Up @@ -167,19 +189,36 @@ type FsCell (value : IConvertible, dataType : DataType, address : FsAddress) =
self.DataType <- otherCell.DataType
self.Value <- otherCell.Value

/// <summary>
/// Copies DataType and Value from this FsCell to a given one and replaces theirs.
/// </summary>
member self.CopyTo(target : FsCell) =
target.DataType <- self.DataType
target.Value <- self.Value

/// <summary>
/// Copies and replaces DataType and Value from a source FsCell into a target FsCell. Returns the target cell.
/// </summary>
static member copy (sourceCell : FsCell) (targetCell : FsCell) =
static member copyFromTo (sourceCell : FsCell) (targetCell : FsCell) =
targetCell.DataType <- sourceCell.DataType
targetCell.Value <- sourceCell.Value
targetCell

/// <summary>
/// Creates a deep copy of this FsCell.
/// </summary>
member self.Copy() =
let value = self.Value
let dt = self.DataType
let addr = self.Address.Copy()
FsCell(value, dt, addr)

/// <summary>
/// Returns a deep copy of a given FsCell.
/// </summary>
static member copy (cell : FsCell) =
cell.Copy()

/// <summary>
/// Gets the cell's value converted to the T type.
/// <para>FsSpreadsheet will try to convert the current value to type 'T.</para>
Expand Down
Loading

0 comments on commit 2069ed9

Please sign in to comment.