{-# LANGUAGE CPP #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
module Codec.Xlsx.Types (
    -- * The main types
    Xlsx(..)
    , Styles(..)
    , DefinedNames(..)
    , ColumnsProperties(..)
    , PageSetup(..)
    , Worksheet(..)
    , CellMap
    , CellValue(..)
    , CellFormula(..)
    , FormulaExpression(..)
    , Cell.SharedFormulaIndex(..)
    , Cell.SharedFormulaOptions(..)
    , Cell(..)
    , RowHeight(..)
    , RowProperties (..)
    -- * Lenses
    -- ** Workbook
    , xlSheets
    , xlStyles
    , xlDefinedNames
    , xlCustomProperties
    , xlDateBase
    -- ** Worksheet
    , wsColumnsProperties
    , wsRowPropertiesMap
    , wsCells
    , wsDrawing
    , wsMerges
    , wsSheetViews
    , wsPageSetup
    , wsConditionalFormattings
    , wsDataValidations
    , wsPivotTables
    , wsAutoFilter
    , wsTables
    , wsProtection
    , wsSharedFormulas
    -- ** Cells
    , Cell.cellValue
    , Cell.cellStyle
    , Cell.cellComment
    , Cell.cellFormula
    -- * Style helpers
    , emptyStyles
    , renderStyleSheet
    , parseStyleSheet
    -- * Misc
    , simpleCellFormula
    , sharedFormulaByIndex
    , def
    , toRows
    , fromRows
    , module X
    ) where

import Control.Exception (SomeException, toException)
#ifdef USE_MICROLENS
import Lens.Micro.TH
#else
import Control.Lens.TH
#endif
import Control.DeepSeq (NFData)
import qualified Data.ByteString.Lazy as L
import Data.Default
import Data.Function (on)
import Data.List (groupBy)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe (catMaybes, isJust)
import Data.Text (Text)
import GHC.Generics (Generic)
import Text.XML (parseLBS, renderLBS)
import Text.XML.Cursor

import Codec.Xlsx.Parser.Internal
import Codec.Xlsx.Types.AutoFilter as X
import Codec.Xlsx.Types.Cell as Cell
import Codec.Xlsx.Types.Comment as X
import Codec.Xlsx.Types.Common as X
import Codec.Xlsx.Types.ConditionalFormatting as X
import Codec.Xlsx.Types.DataValidation as X
import Codec.Xlsx.Types.Drawing as X
import Codec.Xlsx.Types.Drawing.Chart as X
import Codec.Xlsx.Types.Drawing.Common as X
import Codec.Xlsx.Types.PageSetup as X
import Codec.Xlsx.Types.PivotTable as X
import Codec.Xlsx.Types.Protection as X
import Codec.Xlsx.Types.RichText as X
import Codec.Xlsx.Types.SheetViews as X
import Codec.Xlsx.Types.StyleSheet as X
import Codec.Xlsx.Types.Table as X
import Codec.Xlsx.Types.Variant as X
import Codec.Xlsx.Writer.Internal

-- | Height of a row in points (1/72in)
data RowHeight
  = CustomHeight    !Double
    -- ^ Row height is set by the user
  | AutomaticHeight !Double
    -- ^ Row height is set automatically by the program
  deriving (RowHeight -> RowHeight -> Bool
(RowHeight -> RowHeight -> Bool)
-> (RowHeight -> RowHeight -> Bool) -> Eq RowHeight
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RowHeight -> RowHeight -> Bool
$c/= :: RowHeight -> RowHeight -> Bool
== :: RowHeight -> RowHeight -> Bool
$c== :: RowHeight -> RowHeight -> Bool
Eq, Eq RowHeight
Eq RowHeight
-> (RowHeight -> RowHeight -> Ordering)
-> (RowHeight -> RowHeight -> Bool)
-> (RowHeight -> RowHeight -> Bool)
-> (RowHeight -> RowHeight -> Bool)
-> (RowHeight -> RowHeight -> Bool)
-> (RowHeight -> RowHeight -> RowHeight)
-> (RowHeight -> RowHeight -> RowHeight)
-> Ord RowHeight
RowHeight -> RowHeight -> Bool
RowHeight -> RowHeight -> Ordering
RowHeight -> RowHeight -> RowHeight
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: RowHeight -> RowHeight -> RowHeight
$cmin :: RowHeight -> RowHeight -> RowHeight
max :: RowHeight -> RowHeight -> RowHeight
$cmax :: RowHeight -> RowHeight -> RowHeight
>= :: RowHeight -> RowHeight -> Bool
$c>= :: RowHeight -> RowHeight -> Bool
> :: RowHeight -> RowHeight -> Bool
$c> :: RowHeight -> RowHeight -> Bool
<= :: RowHeight -> RowHeight -> Bool
$c<= :: RowHeight -> RowHeight -> Bool
< :: RowHeight -> RowHeight -> Bool
$c< :: RowHeight -> RowHeight -> Bool
compare :: RowHeight -> RowHeight -> Ordering
$ccompare :: RowHeight -> RowHeight -> Ordering
$cp1Ord :: Eq RowHeight
Ord, Int -> RowHeight -> ShowS
[RowHeight] -> ShowS
RowHeight -> String
(Int -> RowHeight -> ShowS)
-> (RowHeight -> String)
-> ([RowHeight] -> ShowS)
-> Show RowHeight
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RowHeight] -> ShowS
$cshowList :: [RowHeight] -> ShowS
show :: RowHeight -> String
$cshow :: RowHeight -> String
showsPrec :: Int -> RowHeight -> ShowS
$cshowsPrec :: Int -> RowHeight -> ShowS
Show, ReadPrec [RowHeight]
ReadPrec RowHeight
Int -> ReadS RowHeight
ReadS [RowHeight]
(Int -> ReadS RowHeight)
-> ReadS [RowHeight]
-> ReadPrec RowHeight
-> ReadPrec [RowHeight]
-> Read RowHeight
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [RowHeight]
$creadListPrec :: ReadPrec [RowHeight]
readPrec :: ReadPrec RowHeight
$creadPrec :: ReadPrec RowHeight
readList :: ReadS [RowHeight]
$creadList :: ReadS [RowHeight]
readsPrec :: Int -> ReadS RowHeight
$creadsPrec :: Int -> ReadS RowHeight
Read, (forall x. RowHeight -> Rep RowHeight x)
-> (forall x. Rep RowHeight x -> RowHeight) -> Generic RowHeight
forall x. Rep RowHeight x -> RowHeight
forall x. RowHeight -> Rep RowHeight x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep RowHeight x -> RowHeight
$cfrom :: forall x. RowHeight -> Rep RowHeight x
Generic)
instance NFData RowHeight

-- | Properties of a row. See §18.3.1.73 "row (Row)" for more details
data RowProperties = RowProps
  { RowProperties -> Maybe RowHeight
rowHeight       :: Maybe RowHeight
    -- ^ Row height in points
  , RowProperties -> Maybe Int
rowStyle        :: Maybe Int
    -- ^ Style to be applied to row
  , RowProperties -> Bool
rowHidden       :: Bool
    -- ^ Whether row is visible or not
  } deriving (RowProperties -> RowProperties -> Bool
(RowProperties -> RowProperties -> Bool)
-> (RowProperties -> RowProperties -> Bool) -> Eq RowProperties
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RowProperties -> RowProperties -> Bool
$c/= :: RowProperties -> RowProperties -> Bool
== :: RowProperties -> RowProperties -> Bool
$c== :: RowProperties -> RowProperties -> Bool
Eq, Eq RowProperties
Eq RowProperties
-> (RowProperties -> RowProperties -> Ordering)
-> (RowProperties -> RowProperties -> Bool)
-> (RowProperties -> RowProperties -> Bool)
-> (RowProperties -> RowProperties -> Bool)
-> (RowProperties -> RowProperties -> Bool)
-> (RowProperties -> RowProperties -> RowProperties)
-> (RowProperties -> RowProperties -> RowProperties)
-> Ord RowProperties
RowProperties -> RowProperties -> Bool
RowProperties -> RowProperties -> Ordering
RowProperties -> RowProperties -> RowProperties
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: RowProperties -> RowProperties -> RowProperties
$cmin :: RowProperties -> RowProperties -> RowProperties
max :: RowProperties -> RowProperties -> RowProperties
$cmax :: RowProperties -> RowProperties -> RowProperties
>= :: RowProperties -> RowProperties -> Bool
$c>= :: RowProperties -> RowProperties -> Bool
> :: RowProperties -> RowProperties -> Bool
$c> :: RowProperties -> RowProperties -> Bool
<= :: RowProperties -> RowProperties -> Bool
$c<= :: RowProperties -> RowProperties -> Bool
< :: RowProperties -> RowProperties -> Bool
$c< :: RowProperties -> RowProperties -> Bool
compare :: RowProperties -> RowProperties -> Ordering
$ccompare :: RowProperties -> RowProperties -> Ordering
$cp1Ord :: Eq RowProperties
Ord, Int -> RowProperties -> ShowS
[RowProperties] -> ShowS
RowProperties -> String
(Int -> RowProperties -> ShowS)
-> (RowProperties -> String)
-> ([RowProperties] -> ShowS)
-> Show RowProperties
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RowProperties] -> ShowS
$cshowList :: [RowProperties] -> ShowS
show :: RowProperties -> String
$cshow :: RowProperties -> String
showsPrec :: Int -> RowProperties -> ShowS
$cshowsPrec :: Int -> RowProperties -> ShowS
Show, ReadPrec [RowProperties]
ReadPrec RowProperties
Int -> ReadS RowProperties
ReadS [RowProperties]
(Int -> ReadS RowProperties)
-> ReadS [RowProperties]
-> ReadPrec RowProperties
-> ReadPrec [RowProperties]
-> Read RowProperties
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [RowProperties]
$creadListPrec :: ReadPrec [RowProperties]
readPrec :: ReadPrec RowProperties
$creadPrec :: ReadPrec RowProperties
readList :: ReadS [RowProperties]
$creadList :: ReadS [RowProperties]
readsPrec :: Int -> ReadS RowProperties
$creadsPrec :: Int -> ReadS RowProperties
Read, (forall x. RowProperties -> Rep RowProperties x)
-> (forall x. Rep RowProperties x -> RowProperties)
-> Generic RowProperties
forall x. Rep RowProperties x -> RowProperties
forall x. RowProperties -> Rep RowProperties x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep RowProperties x -> RowProperties
$cfrom :: forall x. RowProperties -> Rep RowProperties x
Generic)
instance NFData RowProperties

instance Default RowProperties where
  def :: RowProperties
def = RowProps :: Maybe RowHeight -> Maybe Int -> Bool -> RowProperties
RowProps { rowHeight :: Maybe RowHeight
rowHeight       = Maybe RowHeight
forall a. Maybe a
Nothing
                 , rowStyle :: Maybe Int
rowStyle        = Maybe Int
forall a. Maybe a
Nothing
                 , rowHidden :: Bool
rowHidden       = Bool
False
                 }

-- | Column range (from cwMin to cwMax) properties
data ColumnsProperties = ColumnsProperties
  { ColumnsProperties -> Int
cpMin :: Int
  -- ^ First column affected by this 'ColumnWidth' record.
  , ColumnsProperties -> Int
cpMax :: Int
  -- ^ Last column affected by this 'ColumnWidth' record.
  , ColumnsProperties -> Maybe Double
cpWidth :: Maybe Double
  -- ^ Column width measured as the number of characters of the
  -- maximum digit width of the numbers 0, 1, 2, ..., 9 as rendered in
  -- the normal style's font.
  --
  -- See longer description in Section 18.3.1.13 "col (Column Width &
  -- Formatting)" (p. 1605)
  , ColumnsProperties -> Maybe Int
cpStyle :: Maybe Int
  -- ^ Default style for the affected column(s). Affects cells not yet
  -- allocated in the column(s).  In other words, this style applies
  -- to new columns.
  , ColumnsProperties -> Bool
cpHidden :: Bool
  -- ^ Flag indicating if the affected column(s) are hidden on this
  -- worksheet.
  , ColumnsProperties -> Bool
cpCollapsed :: Bool
  -- ^ Flag indicating if the outlining of the affected column(s) is
  -- in the collapsed state.
  , ColumnsProperties -> Bool
cpBestFit :: Bool
  -- ^ Flag indicating if the specified column(s) is set to 'best
  -- fit'.
  } deriving (ColumnsProperties -> ColumnsProperties -> Bool
(ColumnsProperties -> ColumnsProperties -> Bool)
-> (ColumnsProperties -> ColumnsProperties -> Bool)
-> Eq ColumnsProperties
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ColumnsProperties -> ColumnsProperties -> Bool
$c/= :: ColumnsProperties -> ColumnsProperties -> Bool
== :: ColumnsProperties -> ColumnsProperties -> Bool
$c== :: ColumnsProperties -> ColumnsProperties -> Bool
Eq, Int -> ColumnsProperties -> ShowS
[ColumnsProperties] -> ShowS
ColumnsProperties -> String
(Int -> ColumnsProperties -> ShowS)
-> (ColumnsProperties -> String)
-> ([ColumnsProperties] -> ShowS)
-> Show ColumnsProperties
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ColumnsProperties] -> ShowS
$cshowList :: [ColumnsProperties] -> ShowS
show :: ColumnsProperties -> String
$cshow :: ColumnsProperties -> String
showsPrec :: Int -> ColumnsProperties -> ShowS
$cshowsPrec :: Int -> ColumnsProperties -> ShowS
Show, (forall x. ColumnsProperties -> Rep ColumnsProperties x)
-> (forall x. Rep ColumnsProperties x -> ColumnsProperties)
-> Generic ColumnsProperties
forall x. Rep ColumnsProperties x -> ColumnsProperties
forall x. ColumnsProperties -> Rep ColumnsProperties x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep ColumnsProperties x -> ColumnsProperties
$cfrom :: forall x. ColumnsProperties -> Rep ColumnsProperties x
Generic)
instance NFData ColumnsProperties

instance FromCursor ColumnsProperties where
  fromCursor :: Cursor -> [ColumnsProperties]
fromCursor Cursor
c = do
    Int
cpMin <- Name -> Cursor -> [Int]
forall a. FromAttrVal a => Name -> Cursor -> [a]
fromAttribute Name
"min" Cursor
c
    Int
cpMax <- Name -> Cursor -> [Int]
forall a. FromAttrVal a => Name -> Cursor -> [a]
fromAttribute Name
"max" Cursor
c
    Maybe Double
cpWidth <- Name -> Cursor -> [Maybe Double]
forall a. FromAttrVal a => Name -> Cursor -> [Maybe a]
maybeAttribute Name
"width" Cursor
c
    Maybe Int
cpStyle <- Name -> Cursor -> [Maybe Int]
forall a. FromAttrVal a => Name -> Cursor -> [Maybe a]
maybeAttribute Name
"style" Cursor
c
    Bool
cpHidden <- Name -> Bool -> Cursor -> [Bool]
forall a. FromAttrVal a => Name -> a -> Cursor -> [a]
fromAttributeDef Name
"hidden" Bool
False Cursor
c
    Bool
cpCollapsed <- Name -> Bool -> Cursor -> [Bool]
forall a. FromAttrVal a => Name -> a -> Cursor -> [a]
fromAttributeDef Name
"collapsed" Bool
False Cursor
c
    Bool
cpBestFit <- Name -> Bool -> Cursor -> [Bool]
forall a. FromAttrVal a => Name -> a -> Cursor -> [a]
fromAttributeDef Name
"bestFit" Bool
False Cursor
c
    ColumnsProperties -> [ColumnsProperties]
forall (m :: * -> *) a. Monad m => a -> m a
return ColumnsProperties :: Int
-> Int
-> Maybe Double
-> Maybe Int
-> Bool
-> Bool
-> Bool
-> ColumnsProperties
ColumnsProperties {Bool
Int
Maybe Double
Maybe Int
cpBestFit :: Bool
cpCollapsed :: Bool
cpHidden :: Bool
cpStyle :: Maybe Int
cpWidth :: Maybe Double
cpMax :: Int
cpMin :: Int
cpBestFit :: Bool
cpCollapsed :: Bool
cpHidden :: Bool
cpStyle :: Maybe Int
cpWidth :: Maybe Double
cpMax :: Int
cpMin :: Int
..}

instance FromXenoNode ColumnsProperties where
  fromXenoNode :: Node -> Either Text ColumnsProperties
fromXenoNode Node
root = Node
-> AttrParser ColumnsProperties -> Either Text ColumnsProperties
forall a. Node -> AttrParser a -> Either Text a
parseAttributes Node
root (AttrParser ColumnsProperties -> Either Text ColumnsProperties)
-> AttrParser ColumnsProperties -> Either Text ColumnsProperties
forall a b. (a -> b) -> a -> b
$ do
    Int
cpMin <- ByteString -> AttrParser Int
forall a. FromAttrBs a => ByteString -> AttrParser a
fromAttr ByteString
"min"
    Int
cpMax <- ByteString -> AttrParser Int
forall a. FromAttrBs a => ByteString -> AttrParser a
fromAttr ByteString
"max"
    Maybe Double
cpWidth <- ByteString -> AttrParser (Maybe Double)
forall a. FromAttrBs a => ByteString -> AttrParser (Maybe a)
maybeAttr ByteString
"width"
    Maybe Int
cpStyle <- ByteString -> AttrParser (Maybe Int)
forall a. FromAttrBs a => ByteString -> AttrParser (Maybe a)
maybeAttr ByteString
"style"
    Bool
cpHidden <- ByteString -> Bool -> AttrParser Bool
forall a. FromAttrBs a => ByteString -> a -> AttrParser a
fromAttrDef ByteString
"hidden" Bool
False
    Bool
cpCollapsed <- ByteString -> Bool -> AttrParser Bool
forall a. FromAttrBs a => ByteString -> a -> AttrParser a
fromAttrDef ByteString
"collapsed" Bool
False
    Bool
cpBestFit <- ByteString -> Bool -> AttrParser Bool
forall a. FromAttrBs a => ByteString -> a -> AttrParser a
fromAttrDef ByteString
"bestFit" Bool
False
    ColumnsProperties -> AttrParser ColumnsProperties
forall (m :: * -> *) a. Monad m => a -> m a
return ColumnsProperties :: Int
-> Int
-> Maybe Double
-> Maybe Int
-> Bool
-> Bool
-> Bool
-> ColumnsProperties
ColumnsProperties {Bool
Int
Maybe Double
Maybe Int
cpBestFit :: Bool
cpCollapsed :: Bool
cpHidden :: Bool
cpStyle :: Maybe Int
cpWidth :: Maybe Double
cpMax :: Int
cpMin :: Int
cpBestFit :: Bool
cpCollapsed :: Bool
cpHidden :: Bool
cpStyle :: Maybe Int
cpWidth :: Maybe Double
cpMax :: Int
cpMin :: Int
..}

-- | Xlsx worksheet
data Worksheet = Worksheet
  { Worksheet -> [ColumnsProperties]
_wsColumnsProperties :: [ColumnsProperties] -- ^ column widths
  , Worksheet -> Map Int RowProperties
_wsRowPropertiesMap :: Map Int RowProperties -- ^ custom row properties (height, style) map
  , Worksheet -> CellMap
_wsCells :: CellMap -- ^ data mapped by (row, column) pairs
  , Worksheet -> Maybe Drawing
_wsDrawing :: Maybe Drawing -- ^ SpreadsheetML Drawing
  , Worksheet -> [Range]
_wsMerges :: [Range] -- ^ list of cell merges
  , Worksheet -> Maybe [SheetView]
_wsSheetViews :: Maybe [SheetView]
  , Worksheet -> Maybe PageSetup
_wsPageSetup :: Maybe PageSetup
  , Worksheet -> Map SqRef ConditionalFormatting
_wsConditionalFormattings :: Map SqRef ConditionalFormatting
  , Worksheet -> Map SqRef DataValidation
_wsDataValidations :: Map SqRef DataValidation
  , Worksheet -> [PivotTable]
_wsPivotTables :: [PivotTable]
  , Worksheet -> Maybe AutoFilter
_wsAutoFilter :: Maybe AutoFilter
  , Worksheet -> [Table]
_wsTables :: [Table]
  , Worksheet -> Maybe SheetProtection
_wsProtection :: Maybe SheetProtection
  , Worksheet -> Map SharedFormulaIndex SharedFormulaOptions
_wsSharedFormulas :: Map SharedFormulaIndex SharedFormulaOptions
  } deriving (Worksheet -> Worksheet -> Bool
(Worksheet -> Worksheet -> Bool)
-> (Worksheet -> Worksheet -> Bool) -> Eq Worksheet
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Worksheet -> Worksheet -> Bool
$c/= :: Worksheet -> Worksheet -> Bool
== :: Worksheet -> Worksheet -> Bool
$c== :: Worksheet -> Worksheet -> Bool
Eq, Int -> Worksheet -> ShowS
[Worksheet] -> ShowS
Worksheet -> String
(Int -> Worksheet -> ShowS)
-> (Worksheet -> String)
-> ([Worksheet] -> ShowS)
-> Show Worksheet
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Worksheet] -> ShowS
$cshowList :: [Worksheet] -> ShowS
show :: Worksheet -> String
$cshow :: Worksheet -> String
showsPrec :: Int -> Worksheet -> ShowS
$cshowsPrec :: Int -> Worksheet -> ShowS
Show, (forall x. Worksheet -> Rep Worksheet x)
-> (forall x. Rep Worksheet x -> Worksheet) -> Generic Worksheet
forall x. Rep Worksheet x -> Worksheet
forall x. Worksheet -> Rep Worksheet x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Worksheet x -> Worksheet
$cfrom :: forall x. Worksheet -> Rep Worksheet x
Generic)
instance NFData Worksheet

makeLenses ''Worksheet

instance Default Worksheet where
  def :: Worksheet
def =
    Worksheet :: [ColumnsProperties]
-> Map Int RowProperties
-> CellMap
-> Maybe Drawing
-> [Range]
-> Maybe [SheetView]
-> Maybe PageSetup
-> Map SqRef ConditionalFormatting
-> Map SqRef DataValidation
-> [PivotTable]
-> Maybe AutoFilter
-> [Table]
-> Maybe SheetProtection
-> Map SharedFormulaIndex SharedFormulaOptions
-> Worksheet
Worksheet
    { _wsColumnsProperties :: [ColumnsProperties]
_wsColumnsProperties = []
    , _wsRowPropertiesMap :: Map Int RowProperties
_wsRowPropertiesMap = Map Int RowProperties
forall k a. Map k a
M.empty
    , _wsCells :: CellMap
_wsCells = CellMap
forall k a. Map k a
M.empty
    , _wsDrawing :: Maybe Drawing
_wsDrawing = Maybe Drawing
forall a. Maybe a
Nothing
    , _wsMerges :: [Range]
_wsMerges = []
    , _wsSheetViews :: Maybe [SheetView]
_wsSheetViews = Maybe [SheetView]
forall a. Maybe a
Nothing
    , _wsPageSetup :: Maybe PageSetup
_wsPageSetup = Maybe PageSetup
forall a. Maybe a
Nothing
    , _wsConditionalFormattings :: Map SqRef ConditionalFormatting
_wsConditionalFormattings = Map SqRef ConditionalFormatting
forall k a. Map k a
M.empty
    , _wsDataValidations :: Map SqRef DataValidation
_wsDataValidations = Map SqRef DataValidation
forall k a. Map k a
M.empty
    , _wsPivotTables :: [PivotTable]
_wsPivotTables = []
    , _wsAutoFilter :: Maybe AutoFilter
_wsAutoFilter = Maybe AutoFilter
forall a. Maybe a
Nothing
    , _wsTables :: [Table]
_wsTables = []
    , _wsProtection :: Maybe SheetProtection
_wsProtection = Maybe SheetProtection
forall a. Maybe a
Nothing
    , _wsSharedFormulas :: Map SharedFormulaIndex SharedFormulaOptions
_wsSharedFormulas = Map SharedFormulaIndex SharedFormulaOptions
forall k a. Map k a
M.empty
    }

-- | Raw worksheet styles, for structured implementation see 'StyleSheet'
-- and functions in "Codec.Xlsx.Types.StyleSheet"
newtype Styles = Styles {Styles -> ByteString
unStyles :: L.ByteString}
            deriving (Styles -> Styles -> Bool
(Styles -> Styles -> Bool)
-> (Styles -> Styles -> Bool) -> Eq Styles
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Styles -> Styles -> Bool
$c/= :: Styles -> Styles -> Bool
== :: Styles -> Styles -> Bool
$c== :: Styles -> Styles -> Bool
Eq, Int -> Styles -> ShowS
[Styles] -> ShowS
Styles -> String
(Int -> Styles -> ShowS)
-> (Styles -> String) -> ([Styles] -> ShowS) -> Show Styles
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Styles] -> ShowS
$cshowList :: [Styles] -> ShowS
show :: Styles -> String
$cshow :: Styles -> String
showsPrec :: Int -> Styles -> ShowS
$cshowsPrec :: Int -> Styles -> ShowS
Show, (forall x. Styles -> Rep Styles x)
-> (forall x. Rep Styles x -> Styles) -> Generic Styles
forall x. Rep Styles x -> Styles
forall x. Styles -> Rep Styles x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Styles x -> Styles
$cfrom :: forall x. Styles -> Rep Styles x
Generic)
instance NFData Styles

-- | Structured representation of Xlsx file (currently a subset of its contents)
data Xlsx = Xlsx
  { Xlsx -> [(Text, Worksheet)]
_xlSheets :: [(Text, Worksheet)]
  , Xlsx -> Styles
_xlStyles :: Styles
  , Xlsx -> DefinedNames
_xlDefinedNames :: DefinedNames
  , Xlsx -> Map Text Variant
_xlCustomProperties :: Map Text Variant
  , Xlsx -> DateBase
_xlDateBase :: DateBase
  -- ^ date base to use when converting serial value (i.e. 'CellDouble d')
  -- into date-time. Default value is 'DateBase1900'
  --
  -- See also 18.17.4.1 "Date Conversion for Serial Date-Times" (p. 2067)
  } deriving (Xlsx -> Xlsx -> Bool
(Xlsx -> Xlsx -> Bool) -> (Xlsx -> Xlsx -> Bool) -> Eq Xlsx
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Xlsx -> Xlsx -> Bool
$c/= :: Xlsx -> Xlsx -> Bool
== :: Xlsx -> Xlsx -> Bool
$c== :: Xlsx -> Xlsx -> Bool
Eq, Int -> Xlsx -> ShowS
[Xlsx] -> ShowS
Xlsx -> String
(Int -> Xlsx -> ShowS)
-> (Xlsx -> String) -> ([Xlsx] -> ShowS) -> Show Xlsx
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Xlsx] -> ShowS
$cshowList :: [Xlsx] -> ShowS
show :: Xlsx -> String
$cshow :: Xlsx -> String
showsPrec :: Int -> Xlsx -> ShowS
$cshowsPrec :: Int -> Xlsx -> ShowS
Show, (forall x. Xlsx -> Rep Xlsx x)
-> (forall x. Rep Xlsx x -> Xlsx) -> Generic Xlsx
forall x. Rep Xlsx x -> Xlsx
forall x. Xlsx -> Rep Xlsx x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Xlsx x -> Xlsx
$cfrom :: forall x. Xlsx -> Rep Xlsx x
Generic)
instance NFData Xlsx


-- | Defined names
--
-- Each defined name consists of a name, an optional local sheet ID, and a value.
--
-- This element defines the collection of defined names for this workbook.
-- Defined names are descriptive names to represent cells, ranges of cells,
-- formulas, or constant values. Defined names can be used to represent a range
-- on any worksheet.
--
-- Excel also defines a number of reserved names with a special interpretation:
--
-- * @_xlnm.Print_Area@ specifies the workbook's print area.
--   Example value: @SheetName!$A:$A,SheetName!$1:$4@
-- * @_xlnm.Print_Titles@ specifies the row(s) or column(s) to repeat
--   at the top of each printed page.
-- * @_xlnm.Sheet_Title@:refers to a sheet title.
--
-- and others. See Section 18.2.6, "definedNames (Defined Names)" (p. 1728) of
-- the spec (second edition).
--
-- NOTE: Right now this is only a minimal implementation of defined names.
newtype DefinedNames = DefinedNames [(Text, Maybe Text, Text)]
  deriving (DefinedNames -> DefinedNames -> Bool
(DefinedNames -> DefinedNames -> Bool)
-> (DefinedNames -> DefinedNames -> Bool) -> Eq DefinedNames
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DefinedNames -> DefinedNames -> Bool
$c/= :: DefinedNames -> DefinedNames -> Bool
== :: DefinedNames -> DefinedNames -> Bool
$c== :: DefinedNames -> DefinedNames -> Bool
Eq, Int -> DefinedNames -> ShowS
[DefinedNames] -> ShowS
DefinedNames -> String
(Int -> DefinedNames -> ShowS)
-> (DefinedNames -> String)
-> ([DefinedNames] -> ShowS)
-> Show DefinedNames
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DefinedNames] -> ShowS
$cshowList :: [DefinedNames] -> ShowS
show :: DefinedNames -> String
$cshow :: DefinedNames -> String
showsPrec :: Int -> DefinedNames -> ShowS
$cshowsPrec :: Int -> DefinedNames -> ShowS
Show, (forall x. DefinedNames -> Rep DefinedNames x)
-> (forall x. Rep DefinedNames x -> DefinedNames)
-> Generic DefinedNames
forall x. Rep DefinedNames x -> DefinedNames
forall x. DefinedNames -> Rep DefinedNames x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep DefinedNames x -> DefinedNames
$cfrom :: forall x. DefinedNames -> Rep DefinedNames x
Generic)
instance NFData DefinedNames

makeLenses ''Xlsx

instance Default Xlsx where
    def :: Xlsx
def = [(Text, Worksheet)]
-> Styles -> DefinedNames -> Map Text Variant -> DateBase -> Xlsx
Xlsx [] Styles
emptyStyles DefinedNames
forall a. Default a => a
def Map Text Variant
forall k a. Map k a
M.empty DateBase
DateBase1900

instance Default DefinedNames where
    def :: DefinedNames
def = [(Text, Maybe Text, Text)] -> DefinedNames
DefinedNames []

emptyStyles :: Styles
emptyStyles :: Styles
emptyStyles = ByteString -> Styles
Styles ByteString
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"></styleSheet>"

-- | Render 'StyleSheet'
--
-- This is used to render a structured 'StyleSheet' into a raw XML 'Styles'
-- document. Actually /replacing/ 'Styles' with 'StyleSheet' would mean we
-- would need to write a /parser/ for 'StyleSheet' as well (and would moreover
-- require that we support the full style sheet specification, which is still
-- quite a bit of work).
renderStyleSheet :: StyleSheet -> Styles
renderStyleSheet :: StyleSheet -> Styles
renderStyleSheet = ByteString -> Styles
Styles (ByteString -> Styles)
-> (StyleSheet -> ByteString) -> StyleSheet -> Styles
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RenderSettings -> Document -> ByteString
renderLBS RenderSettings
forall a. Default a => a
def (Document -> ByteString)
-> (StyleSheet -> Document) -> StyleSheet -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StyleSheet -> Document
forall a. ToDocument a => a -> Document
toDocument

-- | Parse 'StyleSheet'
--
-- This is used to parse raw 'Styles' into structured 'StyleSheet'
-- currently not all of the style sheet specification is supported
-- so parser (and the data model) is to be completed
parseStyleSheet :: Styles -> Either SomeException StyleSheet
parseStyleSheet :: Styles -> Either SomeException StyleSheet
parseStyleSheet (Styles ByteString
bs) = ParseSettings -> ByteString -> Either SomeException Document
parseLBS ParseSettings
forall a. Default a => a
def ByteString
bs Either SomeException Document
-> (Document -> Either SomeException StyleSheet)
-> Either SomeException StyleSheet
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Document -> Either SomeException StyleSheet
forall b. FromCursor b => Document -> Either SomeException b
parseDoc
  where
    parseDoc :: Document -> Either SomeException b
parseDoc Document
doc = case Cursor -> [b]
forall a. FromCursor a => Cursor -> [a]
fromCursor (Document -> Cursor
fromDocument Document
doc) of
      [b
stylesheet] -> b -> Either SomeException b
forall a b. b -> Either a b
Right b
stylesheet
      [b]
_ -> SomeException -> Either SomeException b
forall a b. a -> Either a b
Left (SomeException -> Either SomeException b)
-> (ParseException -> SomeException)
-> ParseException
-> Either SomeException b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParseException -> SomeException
forall e. Exception e => e -> SomeException
toException (ParseException -> Either SomeException b)
-> ParseException -> Either SomeException b
forall a b. (a -> b) -> a -> b
$ String -> ParseException
ParseException String
"Could not parse style sheets"

-- | converts cells mapped by (row, column) into rows which contain
-- row index and cells as pairs of column indices and cell values
toRows :: CellMap -> [(Int, [(Int, Cell)])]
toRows :: CellMap -> [(Int, [(Int, Cell)])]
toRows CellMap
cells =
    ([((Int, Int), Cell)] -> (Int, [(Int, Cell)]))
-> [[((Int, Int), Cell)]] -> [(Int, [(Int, Cell)])]
forall a b. (a -> b) -> [a] -> [b]
map [((Int, Int), Cell)] -> (Int, [(Int, Cell)])
forall a a b. [((a, a), b)] -> (a, [(a, b)])
extractRow ([[((Int, Int), Cell)]] -> [(Int, [(Int, Cell)])])
-> [[((Int, Int), Cell)]] -> [(Int, [(Int, Cell)])]
forall a b. (a -> b) -> a -> b
$ (((Int, Int), Cell) -> ((Int, Int), Cell) -> Bool)
-> [((Int, Int), Cell)] -> [[((Int, Int), Cell)]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==) (Int -> Int -> Bool)
-> (((Int, Int), Cell) -> Int)
-> ((Int, Int), Cell)
-> ((Int, Int), Cell)
-> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` ((Int, Int) -> Int
forall a b. (a, b) -> a
fst ((Int, Int) -> Int)
-> (((Int, Int), Cell) -> (Int, Int)) -> ((Int, Int), Cell) -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, Int), Cell) -> (Int, Int)
forall a b. (a, b) -> a
fst)) ([((Int, Int), Cell)] -> [[((Int, Int), Cell)]])
-> [((Int, Int), Cell)] -> [[((Int, Int), Cell)]]
forall a b. (a -> b) -> a -> b
$ CellMap -> [((Int, Int), Cell)]
forall k a. Map k a -> [(k, a)]
M.toList CellMap
cells
  where
    extractRow :: [((a, a), b)] -> (a, [(a, b)])
extractRow row :: [((a, a), b)]
row@(((a
x,a
_),b
_):[((a, a), b)]
_) =
        (a
x, (((a, a), b) -> (a, b)) -> [((a, a), b)] -> [(a, b)]
forall a b. (a -> b) -> [a] -> [b]
map (\((a
_,a
y),b
v) -> (a
y,b
v)) [((a, a), b)]
row)
    extractRow [((a, a), b)]
_ = String -> (a, [(a, b)])
forall a. HasCallStack => String -> a
error String
"invalid CellMap row"

-- | reverse to 'toRows'
fromRows :: [(Int, [(Int, Cell)])] -> CellMap
fromRows :: [(Int, [(Int, Cell)])] -> CellMap
fromRows [(Int, [(Int, Cell)])]
rows = [((Int, Int), Cell)] -> CellMap
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([((Int, Int), Cell)] -> CellMap)
-> [((Int, Int), Cell)] -> CellMap
forall a b. (a -> b) -> a -> b
$ ((Int, [(Int, Cell)]) -> [((Int, Int), Cell)])
-> [(Int, [(Int, Cell)])] -> [((Int, Int), Cell)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Int, [(Int, Cell)]) -> [((Int, Int), Cell)]
forall a b b. (a, [(b, b)]) -> [((a, b), b)]
mapRow [(Int, [(Int, Cell)])]
rows
  where
    mapRow :: (a, [(b, b)]) -> [((a, b), b)]
mapRow (a
r, [(b, b)]
cells) = ((b, b) -> ((a, b), b)) -> [(b, b)] -> [((a, b), b)]
forall a b. (a -> b) -> [a] -> [b]
map (\(b
c, b
v) -> ((a
r, b
c), b
v)) [(b, b)]
cells


instance ToElement ColumnsProperties where
  toElement :: Name -> ColumnsProperties -> Element
toElement Name
nm ColumnsProperties {Bool
Int
Maybe Double
Maybe Int
cpBestFit :: Bool
cpCollapsed :: Bool
cpHidden :: Bool
cpStyle :: Maybe Int
cpWidth :: Maybe Double
cpMax :: Int
cpMin :: Int
cpBestFit :: ColumnsProperties -> Bool
cpCollapsed :: ColumnsProperties -> Bool
cpHidden :: ColumnsProperties -> Bool
cpStyle :: ColumnsProperties -> Maybe Int
cpWidth :: ColumnsProperties -> Maybe Double
cpMax :: ColumnsProperties -> Int
cpMin :: ColumnsProperties -> Int
..} = Name -> [(Name, Text)] -> Element
leafElement Name
nm [(Name, Text)]
attrs
    where
      attrs :: [(Name, Text)]
attrs =
        [Name
"min" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Int
cpMin, Name
"max" Name -> Int -> (Name, Text)
forall a. ToAttrVal a => Name -> a -> (Name, Text)
.= Int
cpMax] [(Name, Text)] -> [(Name, Text)] -> [(Name, Text)]
forall a. [a] -> [a] -> [a]
++
        [Maybe (Name, Text)] -> [(Name, Text)]
forall a. [Maybe a] -> [a]
catMaybes
          [ Name
"style" Name -> Maybe Int -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? (Int -> Int -> Maybe Int
forall a. Eq a => a -> a -> Maybe a
justNonDef Int
0 (Int -> Maybe Int) -> Maybe Int -> Maybe Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Maybe Int
cpStyle)
          , Name
"width" Name -> Maybe Double -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Maybe Double
cpWidth
          , Name
"customWidth" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue (Maybe Double -> Bool
forall a. Maybe a -> Bool
isJust Maybe Double
cpWidth)
          , Name
"hidden" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue Bool
cpHidden
          , Name
"collapsed" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue Bool
cpCollapsed
          , Name
"bestFit" Name -> Maybe Bool -> Maybe (Name, Text)
forall a. ToAttrVal a => Name -> Maybe a -> Maybe (Name, Text)
.=? Bool -> Maybe Bool
justTrue Bool
cpBestFit
          ]