content: -- |
  -- basic XmlTree filter
  
  module Text.XML.HXT.DOM.XmlTreeFilter
      ( module Text.XML.HXT.DOM.XmlTreeFilter
      )
  where
  
  import Text.XML.HXT.DOM.XmlTreeTypes
  import Text.XML.HXT.DOM.XmlTreeFunctions
  import Text.XML.HXT.DOM.XmlKeywords
  import Text.XML.HXT.DOM.Unicode ( isXmlSpaceChar        )
  import Text.XML.HXT.DOM.Util    ( decimalStringToInt    )
  
  import Data.List                ( partition     )
  
  infixl 7 += , ++=
  
  -- -----------------------------------------------------------------------------
  --
  -- filter predicates
  
  -- |
  -- test whether the root of a tree contains a document root node.
  
  isRoot                  :: XmlFilter
  isRoot                  = isOfNode (isTagNode t_root)
  
  -- |
  -- test whether the root of a tree contains a tag node.
  --
  -- see also: 'isNsTag'
  
  isTag                   :: String -> XmlFilter
  isTag tn                = isOfNode (isTagNode tn)
  
  -- |
  -- namespace aware test whether the root of a tree contains a tag node. Parameters are the local part and namespace.
  -- Only usable after namespace propagation.
  --
  -- see also: 'isTag'
  
  isNsTag                 :: String -> String -> XmlFilter
  isNsTag lp ns           = isOfTag (\ t -> localPart t == lp && namespaceUri t == ns)
  
  -- |
  -- test whether the root of a tree has a given local name
  -- see also : 'hasNamespace', 'hasPrefix', 'isTag', 'isAttr'
  
  hasLocalPart            :: String -> XmlFilter
  hasLocalPart lp         = isOf ((== lp) . localPartOf)
  
  -- |
  -- test whether the root of a tree has a given prefix name
  -- see also : 'hasNamespace', 'hasLocalPart', 'isTag', 'isAttr'
  
  hasPrefix               :: String -> XmlFilter
  hasPrefix px            = isOf ((== px) . prefixOf)
  
  -- |
  -- test whether the root of a tree belongs to a given namespace
  -- see also : 'isTag', 'isAttr', 'hasLocalPart', 'hasPrefix'
  
  hasNamespace            :: String -> XmlFilter
  hasNamespace ns         = isOf ((== ns) . namespaceOf)
  
  -- |
  -- test whether the root of a tree contains a tag node with a special name.
  
  isOfTag                 :: (TagName -> Bool) -> XmlFilter
  isOfTag                 = isOfNode . isOfTagNode
  
  -- |
  -- test whether the node of a tree is a XTag node or a XPi node with an attibute of a specific name
  --
  -- see also: 'isAttr', 'hasNsAttr'
  
  hasAttr                 :: String -> XmlFilter
  hasAttr an              = (getAttrl .> isAttr an) `guards` this
  
  -- |
  -- test whether the tree is a XTag node with an attibute of a specific local name and namespace uri
  --
  -- see also: 'hasAttr', 'isNsAttr'
  
  hasNsAttr               :: String -> String -> XmlFilter
  hasNsAttr an ns         = (getAttrl .> isNsAttr an ns) `guards` this
  
  -- |
  -- test whether the given node is a XTag node or a XPI node with an attribute with a value with a specific property.
  -- In case of a match, the attribute value represented by a text node is returned as single element list,
  -- else the empty list is the result.
  --
  -- see also : 'getValue'
  
  hasValue                :: String -> (String -> Bool) -> XmlFilter
  hasValue a p t
     | (not . null) att
       &&
       p val
         = xtext val
     | otherwise
         = []
     where
     att = getAttrl .> isAttr a $ t
     val = xshow . getChildren $$ att
  
  -- |
  -- test whether the tree is a processing instruction with a given name.
  
  isPi                    :: String -> XmlFilter
  isPi pn                 = isOfNode (isPiNode pn)
  
  -- |
  -- test whether the tree is a \<?xml ... ?\> declaration
  
  isXmlPi                 :: XmlFilter
  isXmlPi                 = isPi t_xml
  
  -- |
  -- test whether the root of a tree contains a processing instruction of a special name.
  
  isOfPi                  :: (TagName -> Bool) -> XmlFilter
  isOfPi                  = isOfNode . isOfPiNode
  
  -- -----------------------------------------------------------------------------
  
  -- simple XNode Filter predicates
  --
  
  -- |
  -- test whether the root of a tree contains a CDATA node.
  
  isXCdata                :: XmlFilter
  isXCdata                = isOfNode isXCdataNode
  
  -- |
  -- test whether the root of a tree contains a character reference node.
  
  isXCharRef              :: XmlFilter
  isXCharRef              = isOfNode isXCharRefNode
  
  -- |
  -- test whether the root of a tree contains a comment node.
  
  isXCmt                  :: XmlFilter
  isXCmt                  = isOfNode isXCmtNode
  
  -- |
  -- test whether the root of a tree contains a DTD part.
  
  isXDTD                  :: XmlFilter
  isXDTD                  = isOfNode isXDTDNode
  
  -- |
  -- test whether the root of a tree contains an entity reference node.
  
  isXEntityRef            :: XmlFilter
  isXEntityRef            = isOfNode isXEntityRefNode
  
  -- |
  -- test whether the root of a tree contains an error node.
  
  isXError                :: XmlFilter
  isXError                = isOfNode isXErrorNode
  
  -- |
  -- test whether the root of a tree contains a processing instruction node.
  
  isXPi                   :: XmlFilter
  isXPi                   = isOfNode isXPiNode
  
  -- |
  -- test whether the root of a tree contains a tag node.
  
  isXTag                  :: XmlFilter
  isXTag                  = isOfNode isXTagNode
  
  -- |
  -- test whether the root of a tree contains an attribute node.
  
  isXAttr                 :: XmlFilter
  isXAttr                 = isOfNode isXAttrNode
  
  -- |
  -- test whether the root of a tree is an attribute node for a given attribute name
  
  isAttr                  :: String -> XmlFilter
  isAttr an               = isOfNode (isAttrNode an)
  
  -- |
  -- namespace aware test whether the tree contains an attribute node. Parameters are the local part of the atribute name and the namespace.
  -- Only usable after namespace propagation.
  --
  -- see also: 'isNsTag', 'isAttr', 'hasNsAttr'
  
  isNsAttr                :: String -> String -> XmlFilter
  isNsAttr lp ns          = isOfAttr (\ a -> localPart a == lp && namespaceUri a == ns)
  
  -- |
  -- general test for an attribute name
  
  isOfAttr                :: (AttrName -> Bool) -> XmlFilter
  isOfAttr                = isOfNode . isOfAttrNode
  
  -- |
  -- test whether the root of a tree contains a text node.
  
  isXText                 :: XmlFilter
  isXText                 = isOfNode isXTextNode
  
  -- |
  -- test whether the root of a tree contains a special text.
  
  isText                  :: String -> XmlFilter
  isText t                = isOfNode (isTextNode t)
  
  -- |
  -- test whether the root of a tree contains a text node with a special property
  
  isOfText                :: (String -> Bool) -> XmlFilter
  isOfText                = isOfNode . isOfTextNode
  
  -- |
  -- test whether the root of a tree contains a text node only with whitespace.
  
  isWhiteSpace            :: XmlFilter
  isWhiteSpace
      = isOfNode isWS
        where
        isWS n
            = isXTextNode n
              && all isXmlSpaceChar (textOfXNode n)
  
  -- ------------------------------------------------------------
  --
  -- XDTD Predicates
  
  -- |
  -- test whether the root of a tree contains a DOCTYPE DTD part.
  
  isDoctype                       :: XmlFilter
  isDoctype                       = isOfNode (isDTDElemNode DOCTYPE)
  
  -- |
  -- test whether the root of a tree contains an ATTLIST DTD part.
  
  isAttlist                       :: XmlFilter
  isAttlist                       = isOfNode (isDTDElemNode ATTLIST)
  
  -- |
  -- test whether the root of a tree contains an ELEMENT DTD part.
  
  isElement                       :: XmlFilter
  isElement                       = isOfNode (isDTDElemNode ELEMENT)
  
  -- |
  -- test whether the root of a tree contains an ENTITY DTD part.
  
  isEntity                        :: XmlFilter
  isEntity                        = isOfNode (isDTDElemNode ENTITY)
  
  -- |
  -- test whether the root of a tree contains a parameter ENTITY reference.
  
  isPeRef                         :: XmlFilter
  isPeRef                         = isOfNode (isDTDElemNode PEREF)
  
  -- |
  -- test whether the root of a tree contains a DTD name part.
  
  isDTDName                       :: XmlFilter
  isDTDName                       = isOfNode (isDTDElemNode NAME)
  
  -- |
  -- test whether the root of a tree contains a conditional section DTD part.
  
  isCondSect                      :: XmlFilter
  isCondSect                      = isOfNode (isDTDElemNode CONDSECT)
  
  -- |
  -- test whether the root of a tree contains a parameter entity declaration.
  
  isParameterEntity               :: XmlFilter
  isParameterEntity               = isOfNode (isDTDElemNode PENTITY)
  
  -- |
  -- test whether the root of a tree contains a NOTATION DTD part.
  
  isNotation                      :: XmlFilter
  isNotation                      = isOfNode (isDTDElemNode NOTATION)
  
  -- ATTLIST predicates
  
  isDefaultAttrKind               :: XmlFilter
  isDefaultAttrKind               = isOf (a_kind `ofDTDequals` k_default)
  
  isEnumAttrType                  :: XmlFilter
  isEnumAttrType                  = isOf (a_type `ofDTDequals` k_enumeration)
  
  isFixedAttrKind                 :: XmlFilter
  isFixedAttrKind                 = isOf (a_kind `ofDTDequals` k_fixed)
  
  isIdAttrType                    :: XmlFilter
  isIdAttrType                    = isOf (a_type `ofDTDequals` k_id)
  
  isIdRefAttrType                 :: XmlFilter
  isIdRefAttrType                 = isOf ((`elem` [k_idref, k_idrefs]) . valueOfDTD a_type)
  
  isNotationAttrType              :: XmlFilter
  isNotationAttrType              = isOf (a_type `ofDTDequals` k_notation)
  
  isRequiredAttrKind              :: XmlFilter
  isRequiredAttrKind              = isOf (a_kind `ofDTDequals` k_required)
  
  isAttlistParameterEntity        :: XmlFilter
  isAttlistParameterEntity        = isAttlist .> isOf (a_type `ofDTDequals` k_peref)
  
  -- ELEMENT predicates
  
  isEmptyElement                  :: XmlFilter
  isEmptyElement                  = isOf (a_type `ofDTDequals` k_empty)
  
  isMixedContentElement           :: XmlFilter
  isMixedContentElement           = isOf (a_type `ofDTDequals` v_mixed)
  
  isElemWithContent               :: XmlFilter
  isElemWithContent               = isElement
                                    .>
                                    isOf ((`elem` [v_mixed, v_children]) . valueOfDTD a_type)
  
  isAttlistOfElement              :: String -> XmlFilter
  isAttlistOfElement el           = isAttlist
                                    .>
                                    isOf (a_name `ofDTDequals` el)
  
  isElemContentParamEntity        :: XmlFilter
  isElemContentParamEntity        = isElement
                                    .>
                                    isOf (a_type `ofDTDequals` k_peref)
  
  -- ENTITY predicates
  
  isUnparsedEntity                :: XmlFilter
  isUnparsedEntity                = isOf (hasEntry k_ndata . attrlOfDTD)
  
  isExternalParameterEntity       :: XmlFilter
  isExternalParameterEntity       = isParameterEntity
                                    .>
                                    isOf (hasEntry k_system . attrlOfDTD)
  
  isInternalParameterEntity       :: XmlFilter
  isInternalParameterEntity       = isParameterEntity
                                    .> isOf (not . hasEntry k_system . attrlOfDTD)
  
  -- -----------------------------------------------------------------------------
  
  -- |
  -- test whether the root of a tree contains an error node for a warning.
  
  isWarning                       :: XmlFilter
  isWarning                       = isOfNode $ isErrorNode c_warn
  
  -- |
  -- test whether the root of a tree contains an error node for an error.
  
  isError                         :: XmlFilter
  isError                         = isOfNode $ isErrorNode c_err
  
  -- |
  -- test whether the root of a tree contains an error node for a fatal error.
  
  isFatalError                    :: XmlFilter
  isFatalError                    = isOfNode $ isErrorNode c_fatal
  
  -- -----------------------------------------------------------------------------
  --
  -- constructor filter
  
  -- |
  -- constructor filter for a tag node.
  -- a new tree is constructed.
  -- the attributes and the children are computed by applying the aproprate filter to the input tree
  --
  --    * 1.parameter n :  the tag name
  --
  --    - 2.parameter af :  the filter for the attribute list
  --
  --    - 3.parameter cf :  the filter for the children
  --
  --    - returns : the constructor filter
  
  mkXTag          :: String -> XmlFilter -> XmlFilter -> XmlFilter
  mkXTag n af cf
      = \ t -> [ mkXTagTree n (af t) (cf t) ]
  
  -- | Version with qualified names of 'mkXTag'
  
  mkQTag          :: QName -> XmlFilter -> XmlFilter -> XmlFilter
  mkQTag q af cf
      = \ t -> [ mkQTagTree q (af t) (cf t) ]
  
  -- |
  -- constructor filter for a tag node.
  -- a new tree is constructed.
  -- the attributes and the children are computed by applying the aproprate filter to the input tree
  --
  --    * 1.parameter n :  the tag name in form of prefix:localpart
  --
  --    - 2.parameter ns:   the namespace uri
  --
  --    - 3.parameter af :  the filter for the attribute list
  --
  --    - 4.parameter cf :  the filter for the children
  --
  --    - returns : the constructor filter
  
  mkXNsTag        :: String -> String -> XmlFilter -> XmlFilter -> XmlFilter
  mkXNsTag n ns af cf
      = \ t -> [ mkXNsTagTree n ns (af t) (cf t) ]
  
  -- |
  -- filter for attribute construction.
  -- a new tree with attribute name and a value computed by a filter
  -- is build.
  
  mkXAttr         :: String -> XmlFilter -> XmlFilter
  mkXAttr n af
      = \ t -> [ mkXAttrTree n (af t) ]
  
  -- | Qualified version 'mkXAttr'
  
  mkQAttr         :: QName -> XmlFilter -> XmlFilter
  mkQAttr q af
      = \ t -> [ mkQAttrTree q (af t) ]
  
  -- |
  -- filter for attribute construction.
  -- a new tree with attribute name and namespace and a value computed by a filter
  -- is build.
  
  mkXNsAttr       :: String -> String -> XmlFilter -> XmlFilter
  mkXNsAttr n ns af
      = \ t -> [ mkXNsAttrTree n ns (af t) ]
  
  -- |
  -- constructor filter for a text node.
  -- a new tree is constructed.
  -- the input tree is ignored.
  
  mkXText         :: String                          -> XmlFilter
  mkXText         = mkNTree . mkXTextTree
  
  -- |
  -- constructor filter for a character reference node.
  -- a new tree is constructed.
  -- the input tree is ignored.
  
  mkXCharRef      :: Int                             -> XmlFilter
  mkXCharRef      = mkNTree . mkXCharRefTree
  
  -- |
  -- constructor filter for an entity reference node.
  -- a new tree is constructed.
  -- the input tree is ignored.
  
  mkXEntityRef    :: String                          -> XmlFilter
  mkXEntityRef    = mkNTree . mkXEntityRefTree
  
  -- |
  -- constructor filter for a comment node.
  -- a new tree is constructed.
  -- the xml string representation of the filter result
  -- forms the comment
  
  mkXCmt          :: XmlFilter -> XmlFilter
  mkXCmt cf
      = \ t -> [ mkXCmtTree ((xshow . cf) t) ]
  
  -- |
  -- constructor filter for a DTD part.
  -- a new tree is constructed.
  -- the input tree is ignored.
  
  mkXDTD          :: DTDElem -> Attributes -> XmlTrees -> XmlFilter
  mkXDTD n al     = mkNTree . mkXDTDTree n al
  
  -- |
  -- constructor filter for a CDATA section node.
  -- a new tree is constructed.
  -- the input tree is ignored.
  
  mkXCdata        :: XmlFilter -> XmlFilter
  mkXCdata cf
      = \ t -> [ mkXCdataTree ((xshow . cf) t) ]
  
  -- |
  -- constructor filter for a processing instruction
  -- a new tree is constructed from the text representation
  -- of the input tree
  
  mkXPi           :: String -> XmlFilter -> XmlFilter
  mkXPi piName cf
      = \ t -> [ mkXPiTree piName ((xshow . cf) t) ]
  
  -- |
  -- constructor filter for an error message node.
  -- a new tree is constructed.
  -- the input tree is ignored.
  
  mkXError        :: Int -> String                   -> XmlFilter
  mkXError l s    = this . mkXErrorTree l s . this
  
  -- -----------------------------------------------------------------------------
  --
  -- selector filters
  
  -- |
  -- filter for selecting the name of a tag node, an attribute node or a pi node.
  -- Result of the filter is a single element list with a text node or the empty list
  
  getName ::             XmlFilter
  getName t
      | null s    = []
      | otherwise = xtext s
      where
      s = nameOf t
  
  -- |
  -- filter for selecting the attibute list
  
  getAttrl        :: XmlFilter
  getAttrl
      = selAttrl . getNode
      where
      selAttrl (XTag _ al) = al
      selAttrl (XPi  _ al) = al
      selAttrl _           = []
  
  -- |
  -- filter for selecting the value of an attribute in a tag node.
  -- Result of the filter is a single element list with a text node or the empty list
  --
  -- see also : 'hasValue', 'getNsValue'
  
  getValue                :: String -> XmlFilter
  getValue a              = xmlTreesToText . (getAttrl .> isAttr a .> getChildren)
  
  -- |
  -- filter for selecting the value of an attribute with namespace in a tag node.
  -- Result of the filter is a single element list with a text node or the empty list
  --
  -- see also : 'getValue', 'isNsAttr'
  
  getNsValue              :: String -> String -> XmlFilter
  getNsValue lp ns        = xmlTreesToText . (getAttrl .> isNsAttr lp ns .> getChildren)
  
  -- |
  -- filter for selecting an attribute of a DTD node.
  -- Result of the filter is a single element list with a text node or the empty list
  
  getDTDValue             :: String -> XmlFilter
  getDTDValue a           = xtext . valueOfDTD a
  
  -- |
  -- filter for selecting content of a comment.
  -- Result of the filter is a single element list with a text node or the empty list
  
  getXCmt                 :: XmlFilter
  getXCmt
      = selCmt . getNode
        where
        selCmt (XCmt c)   = xtext c
        selCmt _          = []
  
  -- |
  -- filter for selecting the CDATA content.
  -- Result of the filter is a single element list with a text node or the empty list
  
  getXCdata               :: XmlFilter
  getXCdata
      = selData . getNode
        where
        selData (XCdata d)        = xtext d
        selData _                 = []
  
  -- -----------------------------------------------------------------------------
  --
  -- edit filter
  
  -- |
  -- edit filter for changing the name of a tag node, an attribute or a pi.
  -- result of the filter is a single element list with a tag node or the empty list
  
  replaceQName                            :: String -> XmlFilter
  replaceQName n                          = modifyQName (const (mkName n))
  
  -- |
  -- edit filter for changing the text of a text node.
  -- result of the filter is a single element list with a text node or the empty list
  --
  -- example for editing all text nodes of a tree with an edit function @f@:
  --
  -- @processBottomUp (modifyText f \`when\` isXText)@
  
  modifyText                              :: (String -> String) -> XmlFilter
  modifyText f t@(NTree n _)
      | isXTextNode n                     = replaceNode (XText (f (textOfXNode n))) t
  modifyText _ _                          = []
  
  -- |
  -- edit filter for changing the name of a tag node.
  -- result of the filter is a single element list with a text node or the empty list
  
  modifyQName                             ::  (TagName -> TagName) -> XmlFilter
  modifyQName f (NTree (XTag n al) cs)    = [NTree (XTag (f n) al) cs]
  modifyQName f (NTree (XPi  n al) cs)    = [NTree (XPi  (f n) al) cs]
  modifyQName f (NTree (XAttr n)   cs)    = [NTree (XAttr (f n)  ) cs]
  modifyQName _ _                 = []
  
  -- -----------------------------------------------------------------------------
  
  -- |
  -- process the attribute list of a tag node with a tree list filter.
  -- for other trees this filter acts like 'none'
  
  processAttrl                            :: XmlSFilter -> XmlFilter
  processAttrl f t
      = replaceAttrl (f al) t
        where
        al = getAttrl t
  
  -- |
  -- elementwise processing of the attributes of a tag.
  -- for other trees this filter acts like 'none'
  --
  -- see also : 'processAttrl'
  processAttr                             :: XmlFilter -> XmlFilter
  processAttr f
      = processAttrl (f $$)
  
  -- |
  -- replace an attribute list
  -- to be renamed when replaceAttrl is eliminated
  
  replaceAttrl                            :: XmlTrees -> XmlFilter
  replaceAttrl al (NTree (XTag n _al) cs) = [ NTree (XTag n al) cs ]
  replaceAttrl al (NTree (XPi  n _al) cs) = [ NTree (XPi  n al) cs ]
  replaceAttrl _ _                        = []
  
  -- |
  -- delete an attribute from the attribute list of a tag tree
  
  del1Attr                                :: String -> XmlFilter
  del1Attr an                             = processAttr (none `when` isAttr an)
  
  -- |
  -- add an attribute to the attribute list of a tag.
  -- If the attribute already exists, it\'s substituted,
  --
  -- see also: 'sattr', '+='
  
  add1Attr                                :: XmlTree -> XmlFilter
  add1Attr a@(NTree (XAttr an) _av) t
      = replaceAttrl (replace al) t
        where
        al = getAttrl t
        replace [] = [a]
        replace (a1 : as)
            | satisfies (isAttr (qualifiedName an)) a1
                = a : as
            | otherwise
                = a1 : replace as
  
  add1Attr _ t
      = this t
  
  -- |
  -- adds an attribute list computed by a filter, uses 'add1Attr'.
  --
  -- see also: '+='
  
  addAttrl                                :: XmlFilter -> XmlFilter
  addAttrl attrFilter t
      = ((foldl (.>) this . map add1Attr) al) t
        where
        al = attrFilter t
  
  -- |
  -- add or change an attribute with a given string as value for a XTag or XPi tree,
  -- uses 'add1Attr'.
  
  addAttr                                 :: String -> String -> XmlFilter
  addAttr an av
      = add1Attr (mkXAttrTree an (if null av then [] else xtext av))
  
  -- |
  -- add or change an attribute with an Int value.
  -- uses 'addAttr'.
  
  addAttrInt                              :: String -> Int -> XmlFilter
  addAttrInt an av
      = addAttr an (show av)
  
  -- |
  -- edit filter for changing the value of an attribute in a tag node.
  -- result of the filter is a single element list with the tag node or the empty list.
  --
  --    * 1.parameter n :  the name of the attribute
  --
  --    - 2.parameter f :  the edit function for the attribute value
  --
  --    - returns : the edit filter
  
  modifyAttr                              :: String -> (String -> String) -> XmlFilter
  modifyAttr an f
      = processAttr (modifyValue `when` isAttr an)
        where
        modifyValue = modifyChildren ((modifyText f $$) . xmlTreesToText)
  
  -- |
  -- add or change an attribute of a DTD tree
  
  addDTDAttr                              :: String -> String -> XmlFilter
  addDTDAttr an av (NTree (XDTD e al) cs)
      = [NTree (XDTD e (addEntry an av al)) cs]
  
  addDTDAttr _ _ _
      = []
  
  
  -- -----------------------------------------------------------------------------
  --
  
  -- |
  -- convenient function for tag node tree construction
  --
  -- infixl 7
  --
  -- filter combinator for tag tree constrcution
  -- take a 1. filter for computing a tag node tree (or a whole list of tag node trees)
  -- then add all trees computed by the 2. filter to the attribute list when they represent attributes
  -- else append them to the list of children.
  --
  -- if the 1. filter computes a list of tag nodes, the results of the 2. filter are added to all trees
  --
  -- example: @ etag \"a\" += sattr \"href\" \"\#42\" += txt \"the answer\" @
  -- gives the tree @ \<a href=\"\#42\"\>the answer\<\/a\> @
  --
  -- example: @ ( etag \"a\" +++ etag \"b\" ) += sattr \"x\" \"42\" @
  -- gives the tree @ \<a x=\"42\"\/\>\<b x=\"42\"\/\> @
  --
  -- see also : 'etag', 'tag', 'add1Attr', 'modifyChildren', '++='
  
  (+=)    :: XmlFilter -> XmlFilter -> XmlFilter
  (+=) tf cf t
      = concat
        [ ( isXTag `guards` ( addaf .> addcf )) $ t'
          | t' <- tf t
        ]
      where
      (as, cs) = partition (satisfies isXAttr) . cf $ t
  
      addaf | null as   = this
            | otherwise = addAttrl (const as)
  
      addcf | null cs   = this
            | otherwise = modifyChildren (++ cs)
  
  -- |
  -- convenient filter function adding a whole list of trees, just for not writing to many ( ... ).
  --
  -- infixl 7
  --
  -- @ f ++= gl  == f += cat gl @
  --
  -- see also : '+='
  
  (++=)   :: XmlFilter -> [XmlFilter] -> XmlFilter
  tf ++= cfs
      = tf += cat cfs
  
  -- -----------------------------------------------------------------------------
  --
  -- string access functions
  
  -- |
  -- combination of 'getValue' and conversion into a String
  
  valueOf                 :: String -> XmlTree -> String
  valueOf a               = xshow . getValue a
  
  -- |
  -- combination of 'getValue' and conversion to a Int
  
  intValueOf              :: String -> XmlTree -> Int
  intValueOf a            = decimalStringToInt . valueOf a
  
  -- -----------------------------------------------------------------------------
  --
  -- convenient filter (short cuts)
  
  -- |
  -- variant of mkXTag with a list of filters for the attributes and a list of filters for the children.
  -- this variant leads to a more readable source for a complicated construction filter
  -- than the simple solution with a combination of 'mkXTag' and 'cat'.
  --
  -- see also : 'mkXTag', 'stag', 'etag', 'cat', '+='
  
  tag             :: String -> [XmlFilter] -> [XmlFilter] -> XmlFilter
  tag n afs cfs   = mkXTag n (cat afs) (cat cfs)
  
  -- |
  -- variant of tag, useful for tags without attributes and with a list of filters for
  -- constructing the children
  --
  -- see also : 'mkXTag', 'tag', 'etag', 'cat', '+='
  
  stag            :: String -> [XmlFilter] -> XmlFilter
  stag n cfs      = mkXTag n none (cat cfs)
  
  -- |
  -- variant of tag, useful for tags with attributes but without children
  --
  -- see also : 'mkXTag', 'tag', 'stag', 'etag', 'cat'
  
  atag            :: String -> [XmlFilter] -> XmlFilter
  atag n cfs      = mkXTag n (cat cfs) none
  
  -- |
  -- Short cut for empty tags without attributes
  --
  -- see also : 'tag', 'atag', 'stag', 'mkXTag' and '+='
  
  etag            :: String -> XmlFilter
  etag n          = mkXTag n none none
  
  -- | Qualified version of etag
  
  qetag           :: QName -> XmlFilter
  qetag q         = mkQTag q none none
  
  -- | Alias for mkQTag
  
  qtag            :: QName -> XmlFilter -> XmlFilter -> XmlFilter
  qtag = mkQTag
  
  
  -- |
  -- filter for creating a document root node with a list of filters for the attributes and a list of filters for the document.
  --
  -- see also : 'tag'
  
  rootTag         :: [XmlFilter] -> [XmlFilter] -> XmlFilter
  rootTag afs cfs = mkXTag t_root (cat afs) (cat cfs)
  
  -- |
  -- Alias for 'mkXAttr'
  
  attr            :: String -> XmlFilter -> XmlFilter
  attr            = mkXAttr
  
  -- | Alias for mkQAttr
  qattr           :: QName -> XmlFilter -> XmlFilter
  qattr = mkQAttr
  
  -- |
  -- short cut for attribute construction with string constants
  --
  -- set also : 'attr', 'mkXAttr' and 'mkXText'
  
  sattr           :: String -> String -> XmlFilter
  sattr a v       = mkXAttr a (mkXText v)
  
  -- |
  -- short cut for 'mkXText'
  
  txt             :: String  -> XmlFilter
  txt             = mkXText
  
  -- |
  -- short cut for simple comment
  -- the input tree is ignored
  --
  -- see also : 'mkXCmt'
  
  cmt             :: String  -> XmlFilter
  cmt c           = mkXCmt (txt c)
  
  -- |
  -- short cut for generating simple processing instructions (spi)
  -- the input tree is ignored
  --
  -- @spi \"xyz\" \"abc\"@ is equal to @mkXPi \"xyz\" (txt \"abc\")@
  -- (the name pi is already used in prelude)
  
  spi             :: String -> String -> XmlFilter
  spi n c         = mkXPi n (txt c)
  
  -- |
  -- short cut for generating simple cdata sections,
  -- the input tree is ignored
  
  cdata           :: String -> XmlFilter
  cdata s         = mkXCdata (txt s)
  
  -- |
  -- DTD part generation with filter for attributes and children
  -- see also: 'mkXDTDTree'
  
  dtd             :: DTDElem -> [XmlFilter] -> [XmlFilter] -> XmlFilter
  dtd dtdpart afs cfs t
      = mkXDTD dtdpart (toAttrl . cat afs $ t) (cat cfs t) $ t
  
  -- |
  -- short cut for mkXError c_warn.
  --
  -- see also : 'mkXError'
  
  warn            :: String -> XmlFilter
  warn            = mkXError c_warn
  
  -- |
  -- short cut for mkXError c_fatal.
  --
  -- see also : 'mkXError'
  
  err             :: String -> XmlFilter
  err             = mkXError c_err
  
  -- |
  -- short cut for mkXError c_fatal.
  --
  -- see also : 'mkXError'
  
  fatal           ::  String -> XmlFilter
  fatal           = mkXError c_fatal
  
  -- |
  -- check whether an option is set
  --
  -- reads the value of an attribute, usually applied to a document root node,
  -- and checks if the value represents True. The following strings are interpreted
  -- as true: \"1\", \"True\", \"true\", \"yes\", \"Yes\".
  
  hasOption       :: String -> XmlFilter
  hasOption opt
      = getValue opt .> isOfText isSet
        where
        isSet x
            = x `elem` ["1", "True", "true", "yes", "Yes"]
  
  
  -- -----------------------------------------------------------------------------
  
  