-- ------------------------------------------------------------

{- |
   Module     : Text.XML.HXT.DOM.XmlNode
   Copyright  : Copyright (C) 2008 Uwe Schmidt
   License    : MIT


   Maintainer : Uwe Schmidt (uwe@fh-wedel.de)
   Stability  : stable
   Portability: portable

   Interface for XmlArrow to basic data types NTree and XmlTree

   If this module must be used in code working with arrows,
   it should be imported qualified e.g. @as XN@, to prevent name clashes.

   For code working on the \"node and tree level\" this module
   is the interface for writing code without using the
   constructor functions of 'XNode' and 'NTree' directly

-}

-- ------------------------------------------------------------

module Text.XML.HXT.DOM.XmlNode
    ( module Text.XML.HXT.DOM.XmlNode
    , module Data.Tree.NTree.TypeDefs
    )
where

import Control.Monad

import Data.Tree.NTree.TypeDefs

import Data.Maybe

import Text.XML.HXT.DOM.Interface

class XmlNode a where
    -- discriminating predicates

    isText		:: a -> Bool
    isCharRef		:: a -> Bool
    isEntityRef		:: a -> Bool
    isCmt		:: a -> Bool
    isCdata		:: a -> Bool
    isPi		:: a -> Bool
    isElem		:: a -> Bool
    isRoot		:: a -> Bool
    isDTD		:: a -> Bool
    isAttr		:: a -> Bool
    isError		:: a -> Bool

    -- constructor functions for leave nodes

    mkText		:: String -> a
    mkCharRef		:: Int    -> a
    mkEntityRef		:: String -> a
    mkCmt		:: String -> a
    mkCdata		:: String -> a
    mkPi		:: QName  -> XmlTrees -> a
    mkError		:: Int    -> String   -> a

    -- selectors

    getText		:: a -> Maybe String
    getCharRef		:: a -> Maybe Int
    getEntityRef	:: a -> Maybe String
    getCmt		:: a -> Maybe String
    getCdata		:: a -> Maybe String
    getPiName		:: a -> Maybe QName
    getPiContent	:: a -> Maybe XmlTrees
    getElemName		:: a -> Maybe QName
    getAttrl		:: a -> Maybe XmlTrees
    getDTDPart		:: a -> Maybe DTDElem
    getDTDAttrl		:: a -> Maybe Attributes
    getAttrName		:: a -> Maybe QName
    getErrorLevel	:: a -> Maybe Int
    getErrorMsg		:: a -> Maybe String

    -- derived selectors

    getName		:: a -> Maybe QName
    getQualifiedName	:: a -> Maybe String
    getUniversalName	:: a -> Maybe String
    getUniversalUri	:: a -> Maybe String
    getLocalPart	:: a -> Maybe String
    getNamePrefix	:: a -> Maybe String
    getNamespaceUri	:: a -> Maybe String

    -- "modifier" functions

    changeText		:: (String   -> String)   -> a -> a
    changeCmt		:: (String   -> String)   -> a -> a
    changeName		:: (QName    -> QName)    -> a -> a
    changeElemName	:: (QName    -> QName)    -> a -> a
    changeAttrl		:: (XmlTrees -> XmlTrees) -> a -> a
    changeAttrName	:: (QName    -> QName)    -> a -> a
    changePiName	:: (QName    -> QName)    -> a -> a
    changeDTDAttrl	:: (Attributes -> Attributes) -> a -> a

    setText		:: String   -> a -> a
    setCmt		:: String   -> a -> a
    setName		:: QName    -> a -> a
    setElemName		:: QName    -> a -> a
    setElemAttrl	:: XmlTrees -> a -> a
    setAttrName		:: QName    -> a -> a
    setPiName		:: QName    -> a -> a
    setDTDAttrl		:: Attributes -> a -> a

    -- default implementations

    getName n		= getElemName n `mplus` getAttrName n `mplus` getPiName n
    getQualifiedName n	= getName n >>= \ n' -> return (qualifiedName n')
    getUniversalName n	= getName n >>= \ n' -> return (universalName n')
    getUniversalUri n	= getName n >>= \ n' -> return (universalUri n')
    getLocalPart n	= getName n >>= \ n' -> return (localPart n')
    getNamePrefix n	= getName n >>= \ n' -> return (namePrefix n')
    getNamespaceUri n	= getName n >>= \ n' -> return (namespaceUri n')

    setText t		= changeText     (const t)
    setCmt c		= changeCmt      (const c)
    setName n		= changeName     (const n)
    setElemName n	= changeElemName (const n)
    setElemAttrl al	= changeAttrl    (const al)
    setAttrName	n	= changeAttrName (const n)
    setPiName	n	= changePiName   (const n)
    setDTDAttrl	al	= changeDTDAttrl (const al)

-- XNode and XmlTree are instances of XmlNode

instance XmlNode XNode where
    isText (XText _)		= True
    isText _			= False

    isCharRef (XCharRef _)	= True
    isCharRef _			= False

    isEntityRef (XEntityRef _)	= True
    isEntityRef _		= False

    isCmt (XCmt _)		= True
    isCmt _			= False

    isCdata (XCdata _)		= True
    isCdata _			= False

    isPi (XPi _ _)		= True
    isPi _			= False

    isElem (XTag _ _)		= True
    isElem _			= False

    isRoot t                    = isElem t
				  &&
				  fromMaybe "" (getQualifiedName t) == t_root

    isDTD (XDTD _ _)		= True
    isDTD _			= False

    isAttr (XAttr _)		= True
    isAttr _			= False

    isError (XError _ _)	= True
    isError _			= False


    mkText t			= XText t
    mkCharRef c			= XCharRef c
    mkEntityRef e		= XEntityRef e
    mkCmt c			= XCmt c
    mkCdata d			= XCdata d
    mkPi n c			= XPi n (if null c then [] else [mkAttr (mkName a_value) c])
    mkError l msg		= XError l msg


    getText (XText t)		= Just t
    getText _			= Nothing

    getCharRef (XCharRef c)	= Just c
    getCharRef _		= Nothing

    getEntityRef (XEntityRef e)	= Just e
    getEntityRef _		= Nothing

    getCmt (XCmt c)		= Just c
    getCmt _			= Nothing

    getCdata (XCdata d)		= Just d
    getCdata _			= Nothing

    getPiName (XPi n _)		= Just n
    getPiName _			= Nothing

    getPiContent (XPi _ c)	= Just c
    getPiContent _		= Nothing

    getElemName (XTag n _)	= Just n
    getElemName _		= Nothing

    getAttrl (XTag _ al)	= Just al
    getAttrl (XPi  _ al)	= Just al
    getAttrl _			= Nothing

    getDTDPart (XDTD p _)	= Just p
    getDTDPart _		= Nothing

    getDTDAttrl (XDTD _ al)	= Just al
    getDTDAttrl _		= Nothing

    getAttrName (XAttr n)	= Just n
    getAttrName _		= Nothing

    getErrorLevel (XError l _)	= Just l
    getErrorLevel _		= Nothing

    getErrorMsg (XError _ m)	= Just m
    getErrorMsg _		= Nothing

    changeText cf (XText t)		= XText (cf t)
    changeText _ _			= error "changeText undefined"

    changeCmt cf (XCmt c)		= XCmt (cf c)
    changeCmt _ _			= error "changeCmt undefined"

    changeName cf (XTag n al)		= XTag (cf n) al
    changeName cf (XAttr n)		= XAttr (cf n)
    changeName cf (XPi n al)		= XPi (cf n) al
    changeName _ _			= error "changeName undefined"

    changeElemName cf (XTag n al)	= XTag (cf n) al
    changeElemName _ _			= error "changeElemName undefined"

    changeAttrl cf (XTag n al)		= XTag n (cf al)
    changeAttrl cf (XPi  n al)		= XPi  n (cf al)
    changeAttrl _ _			= error "changeAttrl undefined"

    changeAttrName cf (XAttr n)		= XAttr (cf n)
    changeAttrName _ _			= error "changeAttrName undefined"

    changePiName cf (XPi n al)		= XPi (cf n) al
    changePiName _ _			= error "changeAttrName undefined"

    changeDTDAttrl cf (XDTD p al)	= XDTD p (cf al)
    changeDTDAttrl _ _			= error "changeDTDAttrl undefined"

mkElementNode	:: QName -> XmlTrees -> XNode
mkElementNode	= XTag

mkAttrNode	:: QName -> XNode
mkAttrNode	= XAttr

mkDTDNode	:: DTDElem -> Attributes -> XNode
mkDTDNode	= XDTD

instance XmlNode a => XmlNode (NTree a) where
    isText		= isText      . getNode
    isCharRef		= isCharRef   . getNode
    isEntityRef		= isEntityRef . getNode
    isCmt		= isCmt       . getNode
    isCdata		= isCdata     . getNode
    isPi		= isPi        . getNode
    isElem		= isElem      . getNode
    isRoot		= isRoot      . getNode
    isDTD		= isDTD       . getNode
    isAttr		= isAttr      . getNode
    isError		= isError     . getNode

    mkText		= mkLeaf . mkText
    mkCharRef		= mkLeaf . mkCharRef
    mkEntityRef		= mkLeaf . mkEntityRef
    mkCmt		= mkLeaf . mkCmt
    mkCdata		= mkLeaf . mkCdata
    mkPi n		= mkLeaf . mkPi n
    mkError l		= mkLeaf . mkError l

    getText		= getText       . getNode
    getCharRef		= getCharRef    . getNode
    getEntityRef	= getEntityRef  . getNode
    getCmt		= getCmt        . getNode
    getCdata		= getCdata      . getNode
    getPiName		= getPiName     . getNode
    getPiContent	= getPiContent  . getNode
    getElemName		= getElemName   . getNode
    getAttrl		= getAttrl      . getNode
    getDTDPart		= getDTDPart    . getNode
    getDTDAttrl		= getDTDAttrl   . getNode
    getAttrName		= getAttrName   . getNode
    getErrorLevel	= getErrorLevel . getNode
    getErrorMsg		= getErrorMsg   . getNode

    changeText cf	= changeNode (changeText cf)
    changeCmt cf	= changeNode (changeCmt  cf)
    changeName cf	= changeNode (changeName cf)
    changeElemName cf	= changeNode (changeElemName cf)
    changeAttrl cf	= changeNode (changeAttrl cf)
    changeAttrName cf	= changeNode (changeAttrName cf)
    changePiName cf	= changeNode (changePiName cf)
    changeDTDAttrl cf	= changeNode (changeDTDAttrl cf)

mkElement	:: QName -> XmlTrees -> XmlTrees -> XmlTree
mkElement n al	= mkTree (mkElementNode n al)

mkRoot		:: XmlTrees -> XmlTrees -> XmlTree
mkRoot al	= mkTree (mkElementNode (mkSNsName t_root) al)

mkAttr		:: QName -> XmlTrees -> XmlTree
mkAttr n	= mkTree (mkAttrNode n)

mkDTDElem	:: DTDElem -> Attributes -> XmlTrees -> XmlTree
mkDTDElem e al	= mkTree (mkDTDNode e al)

addAttr		:: XmlTree -> XmlTrees -> XmlTrees
addAttr a al
    | isAttr a	= add al
    | otherwise	= al
    where
    an = (qualifiedName . fromJust . getAttrName) a
    add []
	= [a]
    add (a1:al1)
	| isAttr a1
	  &&
	  (qualifiedName . fromJust . getAttrName) a1 == an
	    = a : al1
	| otherwise
	    = a1 : add al1

mergeAttrl	:: XmlTrees -> XmlTrees -> XmlTrees
mergeAttrl 	= foldr addAttr

-- ------------------------------------------------------------