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

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

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

       This module provides functions for validating XML documents represented as
       XmlTree.

       Unlike other popular XML validation tools the validation functions return
       a list of errors instead of aborting after the first error was found.

       Note: The validation process has been split into validation and transformation!
       If @validate@ did not report any errors, @transform@
       should be called, to change the document the way a validating parser
       is expected to do.

    -}

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

    module Text.XML.HXT.DTDValidation.Validation
        ( getDTDSubset
        , validate
        , validateDTD
        , validateDoc
        , removeDoublicateDefs
        , transform
        )

    where

    import Text.XML.HXT.DTDValidation.TypeDefs

    import qualified Text.XML.HXT.DTDValidation.DTDValidation     as DTDValidation
    import qualified Text.XML.HXT.DTDValidation.DocValidation     as DocValidation
    import qualified Text.XML.HXT.DTDValidation.IdValidation      as IdValidation
    import qualified Text.XML.HXT.DTDValidation.DocTransformation as DocTransformation

    -- |
    -- Main validation filter. Check if the DTD and the document are valid.
    --
    --
    --    - returns : a function which expects a complete document as XmlTree input
    --                     and returns a list of all errors found.

    validate        :: XmlArrow
    validate        = validateDTD <+> validateDoc

    -- |
    -- Check if the DTD is valid.
    --
    --
    --    - returns : a function which expects an XmlTree from the parser as input
    --                     and returns a list of all errors found in the DTD.

    validateDTD     :: XmlArrow
    validateDTD     = choiceA
                      [ getDTDSubset        :-> DTDValidation.validateDTD
                      , this                :-> err "Can't validate DTD: There is no DOCTYPE declaration in the document."
                      ]
    -- |
    -- Check if the document corresponds to the given DTD.
    --
    --
    --    - returns : a function which expects a complete document as XmlTree input
    --                     and returns a list of all errors found in the content part.

    validateDoc     :: XmlArrow
    validateDoc
        = validateDoc' $< getDTD
        where
        validateDoc' []             = err "Can't validate document: There is no DOCTYPE declaration in the document."
        validateDoc' (dtdPart:_)    = DocValidation.validateDoc dtdPart
                                      <+>
                                      IdValidation.validateIds  dtdPart

    getDTD          :: XmlArrowS
    getDTD          = listA ( getDTDSubset
                              >>>
                              removeDoublicateDefs
                            )

    -- |
    -- filter for transforming a document with respect to the given DTD.
    --
    -- Validating parsers
    -- are expected to  normalize attribute values and add default values.
    -- This function should be called after a successful validation.
    --
    --
    --    - returns : a function which expects a complete XML document tree
    --                and returns the transformed XmlTree

    transform       :: XmlArrow
    transform       = choiceA
                      [ isRoot      :-> (transformDoc $< getDTD)
                      , this        :-> fatal "Can't transform document: No document root given"
                      ]
                      where
                      transformDoc []       = this
                      transformDoc dtd      = DocTransformation.transform (head dtd)

    -- |
    -- Removes doublicate declarations from the DTD which first declaration is
    -- binding. This is the case for ATTLIST and ENTITY declarations.
    --
    --
    --    - returns : A function that replaces the children of DOCTYPE nodes by a list
    --               where all multiple declarations are removed.

    removeDoublicateDefs    :: XmlArrow
    removeDoublicateDefs    = DTDValidation.removeDoublicateDefs

    --
    -- selects the DTD part of a document
    -- but only, if there is more than the internal part for the 4 predefined XML entities

    getDTDSubset            :: XmlArrow
    getDTDSubset            = getChildren
                              >>>
                              ( filterA $ isDTDDoctype >>> getDTDAttrl >>> isA (hasEntry a_name) )

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