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

    {- |
       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 (mkName 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

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