iyql-0.0.5.1: src/main/haskell/Yql/Core/LocalFunctions/Tables.hs
-- Copyright (c) 2010, Diego Souza
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
-- * Redistributions in binary form must reproduce the above copyright notice,
-- this list of conditions and the following disclaimer in the documentation
-- and/or other materials provided with the distribution.
-- * Neither the name of the <ORGANIZATION> nor the names of its contributors
-- may be used to endorse or promote products derived from this software
-- without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Yql.Core.LocalFunctions.Tables
( function
) where
import Yql.Core.LocalFunction
import Yql.Data.Xml
import Data.List
import qualified Data.Map as M
newtype Column = Column { unColumn :: (String,[Cell]) }
deriving (Show)
data Table = Lines { rows :: [Column] }
deriving (Show)
data Cell = Complex Table
| Scalar String
deriving (Show)
data Doc = Text String Doc
| Line Int Doc
| Space Int Doc
| Nil
-- ctext :: Int -> Char -> Doc
-- ctext k = text . replicate k
-- stext :: Show a => a -> Doc
-- stext = text . show
-- lspace :: Int -> Doc -> Doc
-- lspace by = space by
rspace :: Int -> Doc -> Doc
rspace by = (<> (space by Nil))
text :: String -> Doc
text = flip Text Nil
newline :: Doc -> Doc
newline = Line 0
space :: Int -> Doc -> Doc
space m (Space n d) = Space (m+n) d
space m d = Space m d
nest :: Int -> Doc -> Doc
nest _ Nil = Nil
nest m (Text s d) = Text s (nest m d)
nest m (Line n d) = Line (m+n) (nest m d)
nest m (Space n d) = Space n (nest m d)
empty :: Doc
empty = Nil
width :: Doc -> Int
width = maximum . (0:) . map length . lines . show
cat :: [Doc] -> Doc
cat [] = empty
cat [x] = x
cat (x:xs) = x <> newline (cat xs)
-- columns :: Doc -> Int
-- columns (Text s d) = length s + (columns d)
-- columns (Space m d) = m + (columns d)
-- columns _ = 0
(<>) :: Doc -> Doc -> Doc
(Text s d) <> x = Text s (d <> x)
(Line n d) <> x = Line n (d <> x)
(Space n d) <> x = Space n (d <> x)
Nil <> x = x
infixr 9 <>
render :: Doc -> String
render Nil = ""
render (Space k d) = (replicate k ' ') ++ render d
render (Line k d) = "\n" ++ (replicate k ' ') ++ render d
render (Text s d) = s ++ render d
function :: Exec
function = Transform (const doc) (const $ render . xml2doc)
where doc = unlines [ "Reads the xml output and transform it into tabular form."
]
norm :: Table -> Table
norm (Lines cols) = Lines (map fixColumn cols)
where maxHeight = maximum (map (length . snd . unColumn) cols)
fixHeight xs = (map dig xs) ++ replicate (maxHeight - (length xs)) (Scalar "")
where dig (Complex t) = Complex (norm t)
dig scalar = scalar
fixColumn (Column (h,cs)) = Column (h,fixHeight cs)
showCell :: Cell -> (Int,Doc)
showCell (Scalar s) = (length s,text . unwords . lines $ s)
showCell (Complex t) = let doc = showTable t
in (width doc,doc)
maxWidth :: Column -> Int
maxWidth (Column (h,cs)) = maximum (hSize : map fst cellStr)
where cellStr = map showCell cs
hSize = length h
showColumn :: Column -> [(Int,Doc)]
showColumn (Column (h,cs)) = (myMaxWidth,text header) : map (\(_,b) -> (myMaxWidth,b)) (map showCell cs)
where header = "*" ++ h ++ "*"
myColumn = Column (header,cs)
myMaxWidth = maxWidth myColumn
showTable :: Table -> Doc
showTable = cat . map (showTable_ 1) . transpose . map showColumn . rows
where showTable_ _ [] = text "|"
showTable_ acc ((w,x):xs) = nest acc (text "|"
<> rspace (w - width x) x)
<> showTable_ (acc+w+1) xs
xml2doc :: String -> Doc
xml2doc xml = showTable . xml2table $ results
where Just doc = xmlParse xml
Just results = fmap (childNodes) (findElement "results" doc)
xml2table :: [XML] -> Table
xml2table = unpack . build . xmlRows
where xmlRows tag = map (map xmlCols . filter element . childNodes) tag
xmlCols tag | simple = (tagName tag,[Scalar (verbatim tag)])
| otherwise = (tagName tag,[Complex (xml2table [tag])])
where simple = all pcdata (childNodes tag)
build = foldr (M.unionWith (++)) M.empty . map (M.fromListWith (++))
unpack = norm . Lines . map Column . M.toList
instance Show Doc where
showsPrec _ = showString . render