{- |
Module      :  ./Common/XmlDiff.hs
Description :  compute xml diffs
Copyright   :  (c) Christian Maeder, DFKI GmbH 2011
License     :  GPLv2 or higher, see LICENSE.txt
Maintainer  :  Christian.Maeder@dfki.de
Stability   :  provisional
Portability :  portable

-}

module Common.XmlDiff where

import Common.ToXml
import Common.Utils as Utils
import Common.XPath
import Common.XUpdate

import Common.Lib.MapSet (setToMap)

import Data.List
import qualified Data.Set as Set
import qualified Data.Map as Map

import Text.XML.Light as XML

hetsTags :: UnordTags
hetsTags :: UnordTags
hetsTags = [(QName, Set QName)] -> UnordTags
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
  ([(QName, Set QName)] -> UnordTags)
-> [(QName, Set QName)] -> UnordTags
forall a b. (a -> b) -> a -> b
$ ((String, [String]) -> (QName, Set QName))
-> [(String, [String])] -> [(QName, Set QName)]
forall a b. (a -> b) -> [a] -> [b]
map (\ (e :: String
e, as :: [String]
as) -> (String -> QName
unqual String
e, [QName] -> Set QName
forall a. Ord a => [a] -> Set a
Set.fromList ([QName] -> Set QName) -> [QName] -> Set QName
forall a b. (a -> b) -> a -> b
$ (String -> QName) -> [String] -> [QName]
forall a b. (a -> b) -> [a] -> [b]
map String -> QName
unqual [String]
as))
  [ ("DGNode", ["name"])
  , ("DGLink", ["linkid", "source", "target"])
  , ("Axiom", [])
  , ("Theorem", []) ]
{- for symbols the order matters. For axioms and theorems the names should be
stored separately -}

hetsXmlChanges :: Element -> Element -> [Change]
hetsXmlChanges :: Element -> Element -> [Change]
hetsXmlChanges e1 :: Element
e1 e2 :: Element
e2 = UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff UnordTags
hetsTags [] Count
forall k a. Map k a
Map.empty
  [Element -> Content
Elem (Element -> Content) -> Element -> Content
forall a b. (a -> b) -> a -> b
$ Element -> Element
cleanUpElem Element
e1]
  [Element -> Content
Elem (Element -> Content) -> Element -> Content
forall a b. (a -> b) -> a -> b
$ Element -> Element
cleanUpElem Element
e2]

hetsXmlDiff :: Element -> Element -> Element
hetsXmlDiff :: Element -> Element -> Element
hetsXmlDiff e :: Element
e = [Change] -> Element
mkMods ([Change] -> Element)
-> (Element -> [Change]) -> Element -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Element -> Element -> [Change]
hetsXmlChanges Element
e

{- for elements, whose order does not matter, use the given attribute keys to
determine their equality. An empty set indicates an element that only contains
text to be compared. -}
type UnordTags = Map.Map QName (Set.Set QName)

-- keep track of the nth element with a given tag
type Count = Map.Map QName Int

{- we assume an element contains other elements and no text entries or just a
single text content -}

xmlDiff :: UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff :: UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff m :: UnordTags
m stps :: [Step]
stps em :: Count
em old :: [Content]
old new :: [Content]
new = case ([Content]
old, (Content -> Bool) -> [Content] -> [Content]
forall a. (a -> Bool) -> [a] -> [a]
filter Content -> Bool
validContent [Content]
new) of
  ([], []) -> []
  ([], ns :: [Content]
ns) ->
    [ChangeSel -> Expr -> Change
Change (Insert -> [AddChange] -> ChangeSel
Add Insert
Append ([AddChange] -> ChangeSel) -> [AddChange] -> ChangeSel
forall a b. (a -> b) -> a -> b
$ (Content -> AddChange) -> [Content] -> [AddChange]
forall a b. (a -> b) -> [a] -> [b]
map Content -> AddChange
contentToAddChange [Content]
ns) (Expr -> Change) -> Expr -> Change
forall a b. (a -> b) -> a -> b
$ [Step] -> Expr
pathToExpr [Step]
stps]
  (os :: [Content]
os, []) -> [Step] -> Count -> [Content] -> [Change]
removeIns [Step]
stps Count
em [Content]
os
  (o :: Content
o : os :: [Content]
os, ns :: [Content]
ns@(n :: Content
n : rt :: [Content]
rt)) ->
    if Content -> Bool
validContent Content
o then
    case Content
o of
      Elem e :: Element
e ->
        let en :: QName
en = Element -> QName
elName Element
e
            atts :: [Attr]
atts = Element -> [Attr]
elAttribs Element
e
            cs :: [Content]
cs = Element -> [Content]
elContent Element
e
            (nm :: Count
nm, nstps :: [Step]
nstps) = QName -> Count -> [Step] -> (Count, [Step])
extendPath QName
en Count
em [Step]
stps
            downDiffs :: Element -> [Change]
downDiffs = UnordTags -> [Step] -> [Attr] -> [Content] -> Element -> [Change]
xmlElemDiff UnordTags
m [Step]
nstps [Attr]
atts [Content]
cs
            restDiffs :: [Content] -> [Change]
restDiffs = UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff UnordTags
m [Step]
stps Count
nm [Content]
os
            rmO :: [Change]
rmO = ChangeSel -> Expr -> Change
Change ChangeSel
Remove ([Step] -> Expr
pathToExpr [Step]
nstps) Change -> [Change] -> [Change]
forall a. a -> [a] -> [a]
: [Content] -> [Change]
restDiffs [Content]
ns
        in case QName -> UnordTags -> Maybe (Set QName)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup QName
en UnordTags
m of
        Nothing -> case Content
n of
          Elem e2 :: Element
e2 | Element -> QName
elName Element
e2 QName -> QName -> Bool
forall a. Eq a => a -> a -> Bool
== QName
en ->
             Element -> [Change]
downDiffs Element
e2
             [Change] -> [Change] -> [Change]
forall a. [a] -> [a] -> [a]
++ [Content] -> [Change]
restDiffs [Content]
rt
          _ -> [Change]
rmO
        Just ats :: Set QName
ats -> let
            (mns :: [Content]
mns, rns :: [Content]
rns) = (Content -> Bool) -> [Content] -> ([Content], [Content])
forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (QName -> String -> Map QName String -> Content -> Bool
matchElems QName
en (Element -> String
strContent Element
e) (Map QName String -> Content -> Bool)
-> Map QName String -> Content -> Bool
forall a b. (a -> b) -> a -> b
$
              Map QName String -> Map QName QName -> Map QName String
forall k a b. Ord k => Map k a -> Map k b -> Map k a
Map.intersection ([Attr] -> Map QName String
attrMap [Attr]
atts) (Map QName QName -> Map QName String)
-> Map QName QName -> Map QName String
forall a b. (a -> b) -> a -> b
$ Set QName -> Map QName QName
forall a. Ord a => Set a -> Map a a
setToMap Set QName
ats) [Content]
ns
            in case [Content]
mns of
            Elem mn :: Element
mn : rm :: [Content]
rm -> Element -> [Change]
downDiffs Element
mn
              [Change] -> [Change] -> [Change]
forall a. [a] -> [a] -> [a]
++ [Content] -> [Change]
restDiffs ([Content]
rm [Content] -> [Content] -> [Content]
forall a. [a] -> [a] -> [a]
++ [Content]
rns)
            _ -> [Change]
rmO
      XML.Text cd :: CData
cd -> let inText :: String
inText = CData -> String
cdData CData
cd in case Content
n of
        XML.Text cd2 :: CData
cd2 | String -> String
trim String
inText String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> String
trim String
nText
            -> UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff UnordTags
m [Step]
stps Count
em [Content]
os [Content]
rt
          | Bool
otherwise -> ChangeSel -> Expr -> Change
Change (String -> ChangeSel
Update String
nText) ([Step] -> Expr
pathToExpr [Step]
stps)
              Change -> [Change] -> [Change]
forall a. a -> [a] -> [a]
: UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff UnordTags
m [Step]
stps Count
em [Content]
os [Content]
rt
          where nText :: String
nText = CData -> String
cdData CData
cd2
        _ -> String -> [Change]
forall a. HasCallStack => String -> a
error "xmldiff2"
      _ -> String -> [Change]
forall a. HasCallStack => String -> a
error "xmldiff"
    else UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff UnordTags
m [Step]
stps Count
em [Content]
os [Content]
ns

removeIns :: [Step] -> Count -> [Content] -> [Change]
removeIns :: [Step] -> Count -> [Content] -> [Change]
removeIns stps :: [Step]
stps em :: Count
em cs :: [Content]
cs = case [Content]
cs of
  [] -> []
  c :: Content
c : rs :: [Content]
rs -> case Content
c of
    Elem e :: Element
e -> let
      (nm :: Count
nm, nstps :: [Step]
nstps) = QName -> Count -> [Step] -> (Count, [Step])
extendPath (Element -> QName
elName Element
e) Count
em [Step]
stps
      in ChangeSel -> Expr -> Change
Change ChangeSel
Remove ([Step] -> Expr
pathToExpr [Step]
nstps) Change -> [Change] -> [Change]
forall a. a -> [a] -> [a]
: [Step] -> Count -> [Content] -> [Change]
removeIns [Step]
stps Count
nm [Content]
rs
    _ -> ChangeSel -> Expr -> Change
Change (String -> ChangeSel
Update "") ([Step] -> Expr
pathToExpr [Step]
stps) Change -> [Change] -> [Change]
forall a. a -> [a] -> [a]
: [Step] -> Count -> [Content] -> [Change]
removeIns [Step]
stps Count
em [Content]
rs
         -- does not work for multiple text entries

attrMap :: [Attr] -> Map.Map QName String
attrMap :: [Attr] -> Map QName String
attrMap = [(QName, String)] -> Map QName String
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(QName, String)] -> Map QName String)
-> ([Attr] -> [(QName, String)]) -> [Attr] -> Map QName String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Attr -> (QName, String)) -> [Attr] -> [(QName, String)]
forall a b. (a -> b) -> [a] -> [b]
map (\ a :: Attr
a -> (Attr -> QName
attrKey Attr
a, Attr -> String
attrVal Attr
a))

matchElems :: QName -> String -> Map.Map QName String -> Content -> Bool
matchElems :: QName -> String -> Map QName String -> Content -> Bool
matchElems en :: QName
en t :: String
t atts :: Map QName String
atts c :: Content
c = case Content
c of
  Elem e :: Element
e -> Element -> QName
elName Element
e QName -> QName -> Bool
forall a. Eq a => a -> a -> Bool
== QName
en
    Bool -> Bool -> Bool
&& if Map QName String -> Bool
forall k a. Map k a -> Bool
Map.null Map QName String
atts then [Element] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Element -> [Element]
elChildren Element
e) Bool -> Bool -> Bool
&& Element -> String
strContent Element
e String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
t else
           (String -> String -> Bool)
-> Map QName String -> Map QName String -> Bool
forall k a b.
Ord k =>
(a -> b -> Bool) -> Map k a -> Map k b -> Bool
Map.isSubmapOfBy String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(==) Map QName String
atts ([Attr] -> Map QName String
attrMap ([Attr] -> Map QName String) -> [Attr] -> Map QName String
forall a b. (a -> b) -> a -> b
$ Element -> [Attr]
elAttribs Element
e)
  _ -> Bool
False

xmlElemDiff :: UnordTags -> [Step] -> [Attr] -> [Content] -> Element -> [Change]
xmlElemDiff :: UnordTags -> [Step] -> [Attr] -> [Content] -> Element -> [Change]
xmlElemDiff m :: UnordTags
m nPath :: [Step]
nPath atts :: [Attr]
atts cs :: [Content]
cs e2 :: Element
e2 = [Step] -> [Attr] -> [Attr] -> [Change]
xmlAttrDiff [Step]
nPath [Attr]
atts (Element -> [Attr]
elAttribs Element
e2)
  [Change] -> [Change] -> [Change]
forall a. [a] -> [a] -> [a]
++ UnordTags -> [Step] -> Count -> [Content] -> [Content] -> [Change]
xmlDiff UnordTags
m [Step]
nPath Count
forall k a. Map k a
Map.empty [Content]
cs (Element -> [Content]
elContent Element
e2)

xmlAttrDiff :: [Step] -> [Attr] -> [Attr] -> [Change]
xmlAttrDiff :: [Step] -> [Attr] -> [Attr] -> [Change]
xmlAttrDiff p :: [Step]
p a1 :: [Attr]
a1 a2 :: [Attr]
a2 = let
  m1 :: Map QName String
m1 = [Attr] -> Map QName String
attrMap [Attr]
a1
  m2 :: Map QName String
m2 = [Attr] -> Map QName String
attrMap [Attr]
a2
  rms :: [(QName, String)]
rms = Map QName String -> [(QName, String)]
forall k a. Map k a -> [(k, a)]
Map.toList (Map QName String -> [(QName, String)])
-> Map QName String -> [(QName, String)]
forall a b. (a -> b) -> a -> b
$ Map QName String -> Map QName String -> Map QName String
forall k a b. Ord k => Map k a -> Map k b -> Map k a
Map.difference Map QName String
m1 Map QName String
m2
  ins :: [(QName, String)]
ins = Map QName String -> [(QName, String)]
forall k a. Map k a -> [(k, a)]
Map.toList (Map QName String -> [(QName, String)])
-> Map QName String -> [(QName, String)]
forall a b. (a -> b) -> a -> b
$ Map QName String -> Map QName String -> Map QName String
forall k a b. Ord k => Map k a -> Map k b -> Map k a
Map.difference Map QName String
m2 Map QName String
m1
  inter :: [(QName, (String, String))]
inter = Map QName (String, String) -> [(QName, (String, String))]
forall k a. Map k a -> [(k, a)]
Map.toList (Map QName (String, String) -> [(QName, (String, String))])
-> Map QName (String, String) -> [(QName, (String, String))]
forall a b. (a -> b) -> a -> b
$ ((String, String) -> Bool)
-> Map QName (String, String) -> Map QName (String, String)
forall a k. (a -> Bool) -> Map k a -> Map k a
Map.filter ((String -> String -> Bool) -> (String, String) -> Bool
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(/=))
    (Map QName (String, String) -> Map QName (String, String))
-> Map QName (String, String) -> Map QName (String, String)
forall a b. (a -> b) -> a -> b
$ (String -> String -> (String, String))
-> Map QName String
-> Map QName String
-> Map QName (String, String)
forall k a b c.
Ord k =>
(a -> b -> c) -> Map k a -> Map k b -> Map k c
Map.intersectionWith (,) Map QName String
m1 Map QName String
m2
  addAttrStep :: QName -> Expr
addAttrStep a :: QName
a = [Step] -> Expr
pathToExpr ([Step] -> Expr) -> [Step] -> Expr
forall a b. (a -> b) -> a -> b
$ Axis -> NodeTest -> [Expr] -> Step
Step Axis
Attribute (String -> NodeTest
NameTest (String -> NodeTest) -> String -> NodeTest
forall a b. (a -> b) -> a -> b
$ QName -> String
qName QName
a) [] Step -> [Step] -> [Step]
forall a. a -> [a] -> [a]
: [Step]
p
  in ((QName, String) -> Change) -> [(QName, String)] -> [Change]
forall a b. (a -> b) -> [a] -> [b]
map (ChangeSel -> Expr -> Change
Change ChangeSel
Remove (Expr -> Change)
-> ((QName, String) -> Expr) -> (QName, String) -> Change
forall b c a. (b -> c) -> (a -> b) -> a -> c
. QName -> Expr
addAttrStep (QName -> Expr)
-> ((QName, String) -> QName) -> (QName, String) -> Expr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (QName, String) -> QName
forall a b. (a, b) -> a
fst) [(QName, String)]
rms
     [Change] -> [Change] -> [Change]
forall a. [a] -> [a] -> [a]
++ ((QName, (String, String)) -> Change)
-> [(QName, (String, String))] -> [Change]
forall a b. (a -> b) -> [a] -> [b]
map (\ (a :: QName
a, (_, v :: String
v)) -> ChangeSel -> Expr -> Change
Change (String -> ChangeSel
Update String
v) (Expr -> Change) -> Expr -> Change
forall a b. (a -> b) -> a -> b
$ QName -> Expr
addAttrStep QName
a) [(QName, (String, String))]
inter
     [Change] -> [Change] -> [Change]
forall a. [a] -> [a] -> [a]
++ if [(QName, String)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(QName, String)]
ins then [] else
       [ChangeSel -> Expr -> Change
Change (Insert -> [AddChange] -> ChangeSel
Add Insert
Append ([AddChange] -> ChangeSel) -> [AddChange] -> ChangeSel
forall a b. (a -> b) -> a -> b
$ ((QName, String) -> AddChange) -> [(QName, String)] -> [AddChange]
forall a b. (a -> b) -> [a] -> [b]
map (Attr -> AddChange
AddAttr (Attr -> AddChange)
-> ((QName, String) -> Attr) -> (QName, String) -> AddChange
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (QName -> String -> Attr) -> (QName, String) -> Attr
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry QName -> String -> Attr
Attr) [(QName, String)]
ins) (Expr -> Change) -> Expr -> Change
forall a b. (a -> b) -> a -> b
$ [Step] -> Expr
pathToExpr [Step]
p]

pathToExpr :: [Step] -> Expr
pathToExpr :: [Step] -> Expr
pathToExpr = Maybe Expr -> Path -> Expr
PathExpr Maybe Expr
forall a. Maybe a
Nothing (Path -> Expr) -> ([Step] -> Path) -> [Step] -> Expr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> [Step] -> Path
Path Bool
True ([Step] -> Path) -> ([Step] -> [Step]) -> [Step] -> Path
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Step] -> [Step]
forall a. [a] -> [a]
reverse

extendPath :: QName -> Count -> [Step] -> (Count, [Step])
extendPath :: QName -> Count -> [Step] -> (Count, [Step])
extendPath en :: QName
en em :: Count
em stps :: [Step]
stps = let
  nm :: Count
nm = (Int -> Int -> Int) -> QName -> Int -> Count -> Count
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) QName
en 1 Count
em
  i :: Int
i = Int -> QName -> Count -> Int
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault 1 QName
en Count
nm
  nstps :: [Step]
nstps = Axis -> NodeTest -> [Expr] -> Step
Step Axis
Child (String -> NodeTest
NameTest (String -> NodeTest) -> String -> NodeTest
forall a b. (a -> b) -> a -> b
$ QName -> String
qName QName
en) [PrimKind -> String -> Expr
PrimExpr PrimKind
Number (String -> Expr) -> String -> Expr
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
i] Step -> [Step] -> [Step]
forall a. a -> [a] -> [a]
: [Step]
stps
  in (Count
nm, [Step]
nstps)

-- steps and predicates are reversed!
addPathNumber :: Int -> [Step] -> [Step]
addPathNumber :: Int -> [Step] -> [Step]
addPathNumber i :: Int
i stps :: [Step]
stps =
  let e :: Expr
e = PrimKind -> String -> Expr
PrimExpr PrimKind
Number (String -> Expr) -> String -> Expr
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
i
  in case [Step]
stps of
  [] -> []
  Step a :: Axis
a n :: NodeTest
n es :: [Expr]
es : rs :: [Step]
rs -> Axis -> NodeTest -> [Expr] -> Step
Step Axis
a NodeTest
n (Expr
e Expr -> [Expr] -> [Expr]
forall a. a -> [a] -> [a]
: [Expr]
es) Step -> [Step] -> [Step]
forall a. a -> [a] -> [a]
: [Step]
rs

contentToAddChange :: Content -> AddChange
contentToAddChange :: Content -> AddChange
contentToAddChange c :: Content
c = case Content
c of
  Elem e :: Element
e -> Element -> AddChange
AddElem Element
e
  XML.Text t :: CData
t -> String -> AddChange
AddText (String -> AddChange) -> String -> AddChange
forall a b. (a -> b) -> a -> b
$ CData -> String
cdData CData
t
  CRef s :: String
s -> String -> AddChange
AddText String
s

mkXQName :: String -> QName
mkXQName :: String -> QName
mkXQName s :: String
s = (String -> QName
unqual String
s) { qPrefix :: Maybe String
qPrefix = String -> Maybe String
forall a. a -> Maybe a
Just String
xupdateS }

changeToXml :: Change -> Element
changeToXml :: Change -> Element
changeToXml (Change csel :: ChangeSel
csel pth :: Expr
pth) = let
  sel :: Element -> Element
sel = Attr -> Element -> Element
add_attr (String -> String -> Attr
mkAttr String
selectS (String -> Attr) -> String -> Attr
forall a b. (a -> b) -> a -> b
$ Expr -> String
forall a. Show a => a -> String
show Expr
pth)
  in case ChangeSel
csel of
  Add i :: Insert
i as :: [AddChange]
as -> Element -> Element
sel
    (Element -> Element)
-> ([Content] -> Element) -> [Content] -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. QName -> [Content] -> Element
forall t. Node t => QName -> t -> Element
node (String -> QName
mkXQName (String -> QName) -> String -> QName
forall a b. (a -> b) -> a -> b
$ Insert -> String
showInsert Insert
i) ([Content] -> Element) -> [Content] -> Element
forall a b. (a -> b) -> a -> b
$ (AddChange -> Content) -> [AddChange] -> [Content]
forall a b. (a -> b) -> [a] -> [b]
map AddChange -> Content
addsToXml [AddChange]
as
  Remove -> Element -> Element
sel (Element -> Element) -> Element -> Element
forall a b. (a -> b) -> a -> b
$ QName -> () -> Element
forall t. Node t => QName -> t -> Element
node (String -> QName
mkXQName String
removeS) ()
  Update s :: String
s -> Element -> Element
sel (Element -> Element) -> Element -> Element
forall a b. (a -> b) -> a -> b
$ QName -> String -> Element
forall t. Node t => QName -> t -> Element
node (String -> QName
mkXQName String
updateS) String
s
  _ -> String -> Element
forall a. HasCallStack => String -> a
error "changeToXml"

addsToXml :: AddChange -> Content
addsToXml :: AddChange -> Content
addsToXml a :: AddChange
a = case AddChange
a of
  AddElem e :: Element
e -> Element -> Content
Elem (Element -> Content) -> Element -> Content
forall a b. (a -> b) -> a -> b
$ Element -> Element
cleanUpElem Element
e
  AddAttr (Attr k :: QName
k v :: String
v) -> Element -> Content
Elem
    (Element -> Content) -> (Element -> Element) -> Element -> Content
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Attr -> Element -> Element
add_attr (String -> Attr
mkNameAttr (String -> Attr) -> String -> Attr
forall a b. (a -> b) -> a -> b
$ QName -> String
qName QName
k) (Element -> Content) -> Element -> Content
forall a b. (a -> b) -> a -> b
$ QName -> String -> Element
forall t. Node t => QName -> t -> Element
node (String -> QName
mkXQName String
attributeS) String
v
  AddText s :: String
s -> String -> Content
mkText String
s
  _ -> String -> Content
forall a. HasCallStack => String -> a
error "addsToXml"

mkMods :: [Change] -> Element
mkMods :: [Change] -> Element
mkMods = QName -> [Element] -> Element
forall t. Node t => QName -> t -> Element
node (String -> QName
mkXQName "modifications") ([Element] -> Element)
-> ([Change] -> [Element]) -> [Change] -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Change -> Element) -> [Change] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
map Change -> Element
changeToXml