packages feed

xslt-0.1: src/Text/XML/XSLT.hs

module Text.XML.XSLT
    ( Stylesheet
    , parseFile
    , parseFile_
    , applyStylesheet
    , saveResultToString
    , saveResultToString_
    , cleanupGlobals
    , freeStylesheet
    ) where

import Foreign.C
import Foreign
import Control.Monad

import qualified Data.ByteString as BS
import qualified Data.ByteString.Base as BS
import Data.ByteString (ByteString)

import qualified Text.XML.LibXML as XML
import qualified Text.XML.LibXML.Internals as XML
import Text.XML.LibXML (Document)

newtype Stylesheet = Stylesheet (ForeignPtr Stylesheet)

freeStylesheet :: Stylesheet -> IO ()
freeStylesheet (Stylesheet ptr) =
#if defined(__GLASGOW_HASKELL__)
  finalizeForeignPtr ptr
#else
  return ()
#endif


-- xsltStylesheetPtr xsltParseStylesheetFile(const xmlChar * filename)
foreign import ccall unsafe xsltParseStylesheetFile :: CString -> IO (Ptr Stylesheet)

parseFile :: FilePath -> IO Stylesheet
parseFile path
    = do mbSheet <- parseFile_ path
         case mbSheet of
           Nothing -> error $ "Text.XML.XSLT.parseFile: failed to parse input from: " ++ path
           Just sheet -> return sheet

parseFile_ :: FilePath -> IO (Maybe Stylesheet)
parseFile_ path
    = do ptr <- xsltParseStylesheetFile =<< newCString path
         if ptr == nullPtr
            then return Nothing
            else liftM Just $ mkFinalizedStylesheet ptr

-- xmlDocPtr xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, const char ** params)
foreign import ccall unsafe xsltApplyStylesheet :: Ptr Stylesheet -> Ptr Document -> Ptr CString -> IO (Ptr Document)

-- FIXME: don't ignore the params
applyStylesheet :: Stylesheet -> Maybe Document -> [(ByteString,ByteString)] -> IO Document
applyStylesheet (Stylesheet stylesheet) mbDocument _params
    = withForeignPtr stylesheet $ \sheetptr ->
      withMbDoc mbDocument $ \docPtr ->
      do ptr <- xsltApplyStylesheet sheetptr docPtr nullPtr
         XML.mkFinalizedDocument ptr
    where withMbDoc Nothing x = x nullPtr
          withMbDoc (Just doc) x = XML.withDocument doc x

--int xsltSaveResultToString(xmlChar ** doc_txt_ptr, 
--                           int * doc_txt_len, 
--                            xmlDocPtr result, 
--                                       xsltStylesheetPtr style)
foreign import ccall unsafe xsltSaveResultToString
    :: Ptr CString -> Ptr CInt -> Ptr Document -> Ptr Stylesheet -> IO CInt

saveResultToString :: Document -> Stylesheet -> IO ByteString
saveResultToString doc stylesheet
    = do mbStr <- saveResultToString_ doc stylesheet
         case mbStr of
           Nothing -> error "Text.XML.XSLT.saveResultToString: failed to save result"
           Just str -> return str

saveResultToString_ :: Document -> Stylesheet -> IO (Maybe ByteString)
saveResultToString_ doc (Stylesheet sheet)
    = withForeignPtr sheet $ \sheetPtr ->
      XML.withDocument doc $ \docPtr ->
      alloca $ \strPtr ->
      alloca $ \lenPtr ->
      do ret <- xsltSaveResultToString strPtr lenPtr docPtr sheetPtr
         case ret of
           (-1) -> return Nothing
           _    -> do cstr <- peek strPtr
                      len  <- peek lenPtr
                      fp <- newForeignPtr finalizerFree (castPtr cstr)
                      return $! Just $! BS.PS fp 0 (fromIntegral len)

foreign import ccall unsafe "xsltCleanupGlobals" cleanupGlobals :: IO ()

foreign import ccall unsafe "&xsltFreeStylesheet" xsltFreeStylesheet :: FunPtr (Ptr Stylesheet -> IO ())

mkFinalizedStylesheet :: Ptr Stylesheet -> IO Stylesheet
mkFinalizedStylesheet = liftM Stylesheet . newForeignPtr xsltFreeStylesheet