module Codec.Xlsx.Types.Common
( CellRef(..)
, singleCellRef
, fromSingleCellRef
, Range
, mkRange
, fromRange
, SqRef(..)
, XlsxText(..)
, Formula(..)
, CellValue(..)
, int2col
, col2int
) where
import Control.Arrow
import Control.Monad (guard)
import Data.Char
import Data.Ix (inRange)
import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as T
import Text.XML
import Text.XML.Cursor
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Codec.Xlsx.Parser.Internal
import Codec.Xlsx.Types.RichText
import Codec.Xlsx.Writer.Internal
int2col :: Int -> Text
int2col = T.pack . reverse . map int2let . base26
where
int2let 0 = 'Z'
int2let x = chr $ (x 1) + ord 'A'
base26 0 = []
base26 i = let i' = (i `mod` 26)
i'' = if i' == 0 then 26 else i'
in seq i' (i' : base26 ((i i'') `div` 26))
col2int :: Text -> Int
col2int = T.foldl' (\i c -> i * 26 + let2int c) 0
where
let2int c = 1 + ord c ord 'A'
newtype CellRef = CellRef
{ unCellRef :: Text
} deriving (Eq, Ord, Show)
singleCellRef :: (Int, Int) -> CellRef
singleCellRef = CellRef . singleCellRefRaw
singleCellRefRaw :: (Int, Int) -> Text
singleCellRefRaw (row, col) = T.concat [int2col col, T.pack (show row)]
fromSingleCellRef :: CellRef -> Maybe (Int, Int)
fromSingleCellRef = fromSingleCellRefRaw . unCellRef
fromSingleCellRefRaw :: Text -> Maybe (Int, Int)
fromSingleCellRefRaw t = do
let (colT, rowT) = T.span (inRange ('A', 'Z')) t
guard $ not (T.null colT) && not (T.null rowT) && T.all isDigit rowT
row <- decimal rowT
return (row, col2int colT)
type Range = CellRef
mkRange :: (Int, Int) -> (Int, Int) -> Range
mkRange fr to = CellRef $ T.concat [singleCellRefRaw fr, T.pack ":", singleCellRefRaw to]
fromRange :: Range -> Maybe ((Int, Int), (Int, Int))
fromRange r =
case T.split (== ':') (unCellRef r) of
[from, to] -> (,) <$> fromSingleCellRefRaw from <*> fromSingleCellRefRaw to
_ -> Nothing
newtype SqRef = SqRef [CellRef]
deriving (Eq, Ord, Show)
data XlsxText = XlsxText Text
| XlsxRichText [RichTextRun]
deriving (Show, Eq, Ord)
newtype Formula = Formula {unFormula :: Text}
deriving (Eq, Ord, Show)
data CellValue
= CellText Text
| CellDouble Double
| CellBool Bool
| CellRich [RichTextRun]
deriving (Eq, Ord, Show)
instance FromCursor XlsxText where
fromCursor cur = do
let
ts = cur $/ element (n_ "t") >=> contentOrEmpty
rs = cur $/ element (n_ "r") >=> fromCursor
case (ts,rs) of
([t], []) ->
return $ XlsxText t
([], _:_) ->
return $ XlsxRichText rs
_ ->
fail "invalid item"
instance FromAttrVal CellRef where
fromAttrVal = fmap (first CellRef) . fromAttrVal
instance FromAttrVal SqRef where
fromAttrVal t = do
rs <- mapM (fmap fst . fromAttrVal) $ T.split (== ' ') t
readSuccess $ SqRef rs
instance FromCursor Formula where
fromCursor cur = [Formula . T.concat $ cur $/ content]
instance ToElement XlsxText where
toElement nm si = Element {
elementName = nm
, elementAttributes = Map.empty
, elementNodes = map NodeElement $
case si of
XlsxText text -> [elementContent "t" text]
XlsxRichText rich -> map (toElement "r") rich
}
instance ToAttrVal CellRef where
toAttrVal = toAttrVal . unCellRef
instance ToAttrVal SqRef where
toAttrVal (SqRef refs) = T.intercalate " " $ map toAttrVal refs
instance ToElement Formula where
toElement nm (Formula txt) = elementContent nm txt