#cs(module modif mzscheme
(require (planet "ssax.ss" ("lizorkin" "ssax.plt" 1 2)))
(require "sxpathlib.ss")
(require "sxml-tools.ss")
(require "xpath-context_xlink.ss")
(require "xpath-ast.ss")
(require "ddo-txpath.ss")
(define (sxml:modification-error . text)
(cerr "Modification error: ")
(apply cerr text)
(cerr nl)
#f)
(define (sxml:separate-list pred? lst)
(let loop ((lst lst)
(satisfy '())
(rest '()))
(cond
((null? lst)
(values (reverse satisfy) (reverse rest)))
((pred? (car lst)) (loop (cdr lst)
(cons (car lst) satisfy) rest))
(else
(loop (cdr lst)
satisfy (cons (car lst) rest))))))
(define (sxml:assert-proper-attribute obj)
(if
(or (and (pair? obj) (not (null? obj))
(eq? (car obj) '@))
(and (list? obj) (symbol? (car obj))
(or (null? (cdr obj)) (null? (cddr obj)))))
#t
(sxml:modification-error
"improper attribute node - " obj)))
(define (sxml:unite-annot-attributes-lists . annot-attributes-lst)
(if
(null? annot-attributes-lst) '()
(let iter-lst ((src annot-attributes-lst)
(attrs '())
(annotations '()))
(if
(null? src) (if (null? annotations)
(cons '@ (reverse attrs))
`(@ ,@(reverse attrs) (@ ,@annotations)))
(let iter-annot-attrs ((annot-attrs (cdar src))
(attrs attrs)
(annotations annotations))
(if
(null? annot-attrs) (iter-lst (cdr src) attrs annotations)
(let ((curr (car annot-attrs)))
(cond
((and (pair? curr)
(not (null? curr))
(eq? (car curr) '@))
(iter-annot-attrs (cdr annot-attrs)
attrs
(append annotations (cdr curr))))
((sxml:assert-proper-attribute curr)
(if
(assq (car curr) attrs) (sxml:modification-error
"duplicate attribute - " (car curr))
(iter-annot-attrs (cdr annot-attrs)
(cons curr attrs)
annotations)))
(else #f)))))))))
(define (sxml:tree-trans curr-node targets-alist)
(call-with-values
(lambda () (sxml:separate-list
(lambda (pair) (null? (car pair)))
targets-alist))
(lambda (matched targets-alist )
(and-let*
((after-subnodes (if
(or (not (pair? curr-node)) (null? targets-alist) )
curr-node
(let process-attrs ((targets-alist targets-alist)
(src-attrs (sxml:attr-list curr-node))
(res-attrs '()))
(if
(null? src-attrs) (if
(null? targets-alist) (cons (car curr-node) ((lambda (kids)
(if (null? res-attrs) kids
(cons (cons '@ (reverse res-attrs))
kids)))
((if (and (not (null? (cdr curr-node)))
(pair? (cadr curr-node))
(eq? (caadr curr-node) '@))
cddr cdr)
curr-node)))
(let process-kids ((targets-alist targets-alist)
(src-kids (cdr curr-node))
(res-kids '()))
(cond
((null? src-kids) (call-with-values
(lambda () (sxml:separate-list
(lambda (obj)
(and (pair? obj) (eq? (car obj) '@)))
res-kids))
(lambda (more-attrs kids)
(if
(and (null? res-attrs) (null? more-attrs))
(cons (car curr-node) kids)
(and-let*
((overall-attrs
(apply
sxml:unite-annot-attributes-lists
(cons
(cons '@ (reverse res-attrs))
more-attrs))))
(cons (car curr-node) (cons overall-attrs kids)))))))
((and (pair? (car src-kids))
(eq? (caar src-kids) '@))
(process-kids
targets-alist (cdr src-kids) res-kids))
(else
(let ((kid-templates
(filter
(lambda (pair)
(eq? (caar pair) (car src-kids)))
targets-alist)))
(if
(null? kid-templates)
(process-kids
targets-alist
(cdr src-kids)
(append res-kids (list (car src-kids))))
(and-let*
((new-kid
(sxml:tree-trans
(car src-kids)
(map
(lambda (pair)
(cons (cdar pair) (cdr pair)))
kid-templates))))
(process-kids
(filter
(lambda (pair)
(not (eq? (caar pair) (car src-kids))))
targets-alist)
(cdr src-kids)
(append
res-kids
(if (nodeset? new-kid)
new-kid
(list new-kid)))))))))))
(let* ((curr-attr (car src-attrs))
(attr-templates
(filter
(lambda (pair)
(eq? (caar pair) curr-attr))
targets-alist)))
(if
(null? attr-templates)
(process-attrs targets-alist
(cdr src-attrs)
(cons curr-attr res-attrs))
(let ((new-attr (sxml:tree-trans
curr-attr
(map
(lambda (pair)
(cons (cdar pair) (cdr pair)))
attr-templates))))
(process-attrs
(filter
(lambda (pair)
(not (eq? (caar pair) curr-attr)))
targets-alist)
(cdr src-attrs)
(if (nodeset? new-attr)
(append (reverse new-attr) res-attrs)
(cons new-attr res-attrs)))))))))))
(let process-this ((new-curr-node after-subnodes)
(curr-handlers (map cdr matched)))
(if
(null? curr-handlers)
(if (not (pair? new-curr-node))
new-curr-node (call-with-values (lambda () (sxml:separate-list
(lambda (obj) (and (pair? obj) (eq? (car obj) '@)))
(cdr new-curr-node)))
(lambda (attrs kids)
(if (null? attrs)
new-curr-node (and-let*
((overall-attrs
(apply sxml:unite-annot-attributes-lists attrs)))
(cons
(car new-curr-node) (cons overall-attrs kids)))))))
(process-this
((cadar curr-handlers) new-curr-node
(caar curr-handlers) (caddar curr-handlers) )
(cdr curr-handlers))))))))
(define (sxml:transform-document doc update-targets)
(let ((targets-alist
(map-union
(lambda (triple)
(let ((node-path (reverse (sxml:context->content (car triple)))))
(if
(eq? (car node-path) doc)
(list (cons (cdr node-path) triple))
'())))
update-targets)))
(if (null? targets-alist) doc
(sxml:tree-trans doc targets-alist))))
(define (sxml:lambdas-upd-specifiers->targets doc lambdas-upd-specifiers)
(let ((doc-list (list doc)))
(letrec
((construct-targets
(lambda (base-cntxtset lambdas-upd-specifiers)
(let ((triple (car lambdas-upd-specifiers)))
(let iter-base ((base-cntxtset base-cntxtset)
(res '())
(new-base '()))
(if
(null? base-cntxtset) (if
(null? (cdr lambdas-upd-specifiers)) res
(append
res
(construct-targets
(if
(cadadr lambdas-upd-specifiers) (apply ddo:unite-multiple-context-sets new-base)
doc-list)
(cdr lambdas-upd-specifiers))))
(let* ((curr-base-context (car base-cntxtset))
(context-set ((car triple)
(list curr-base-context)
(cons 1 1)
'() )))
(iter-base
(cdr base-cntxtset)
(append res
(map
(lambda (context)
(list context
(caddr triple) (sxml:context->node curr-base-context)))
context-set))
(cons context-set new-base)))))))))
(if
(null? lambdas-upd-specifiers) '()
(construct-targets doc-list lambdas-upd-specifiers)))))
(define (sxml:update-specifiers->lambdas update-specifiers)
(let iter ((src update-specifiers)
(res '()))
(if
(null? src) (reverse res)
(let ((curr (car src)))
(if
(or (not (list? curr))
(null? (cdr curr)))
(sxml:modification-error "improper update-specifier: " curr)
(and-let*
((ast (txp:xpath->ast (car curr))))
(call-with-values
(lambda ()
(if
(eq? (car ast) 'absolute-location-path)
(values
(ddo:ast-relative-location-path
(cons 'relative-location-path (cdr ast))
#f #t 0 '(0) )
#f)
(values
(ddo:ast-relative-location-path ast #f #t 0 '(0))
(not (null? res)) )))
(lambda (txpath-pair relative?)
(if
(not txpath-pair) txpath-pair (let ((txpath-lambda (car txpath-pair))
(action (cadr curr)))
(if
(procedure? action) (iter (cdr src)
(cons
(list txpath-lambda relative? action)
res))
(case action
((delete delete-undeep)
(iter (cdr src)
(cons
(list
txpath-lambda
relative?
(cdr
(assq action
`((delete . ,modif:delete)
(delete-undeep . ,modif:delete-undeep)))))
res)))
((insert-into insert-following insert-preceding)
(let ((params (cddr curr)))
(iter (cdr src)
(cons
(list
txpath-lambda
relative?
((cdr
(assq
action
`((insert-into . ,modif:insert-into)
(insert-following . ,modif:insert-following)
(insert-preceding . ,modif:insert-preceding))))
(lambda (context base-node) params)))
res))))
((replace)
(let ((params (cddr curr)))
(iter (cdr src)
(cons
(list txpath-lambda relative?
(lambda (node context base-node) params))
res))))
((rename)
(if
(or (null? (cddr curr)) (not (symbol? (caddr curr))))
(sxml:modification-error
"improper new name for the node to be renamed: "
curr)
(iter
(cdr src)
(cons
(let ((new-name (caddr curr)))
(list txpath-lambda relative? (modif:rename new-name)))
res))))
((move-into move-following move-preceding)
(if
(or (null? (cddr curr)) (not (string? (caddr curr))))
(sxml:modification-error
"improper destination location path for move action: "
curr)
(and-let*
((ast (txp:xpath->ast (caddr curr)))
(txpath-pair (ddo:ast-location-path ast #f #t 0 '(0))))
(iter (cdr src)
(cons
(list
(car txpath-pair)
#t
((cdr
(assq
action
`((move-into . ,modif:insert-into)
(move-following . ,modif:insert-following)
(move-preceding . ,modif:insert-preceding))))
(lambda (context base-node) base-node)))
(cons
(list txpath-lambda relative? modif:delete)
res))))))
(else
(sxml:modification-error "unknown action: " curr))))))))))))))
(define (modif:insert-following node-specifier)
(lambda (node context base-node)
((if (nodeset? node) append cons)
node
(as-nodeset (node-specifier context base-node)))))
(define (modif:insert-preceding node-specifier)
(lambda (node context base-node)
(let ((new (node-specifier context base-node)))
((if (nodeset? new) append cons)
new
(as-nodeset node)))))
(define (modif:insert-into node-specifier)
(lambda (node context base-node)
(let* ((to-insert (as-nodeset (node-specifier context base-node)))
(insert-into-single (lambda (node)
(if (not (pair? node)) node
(append node to-insert)))))
(if (nodeset? node)
(map insert-into-single node)
(insert-into-single node)))))
(define (modif:rename new-name)
(let ((rename-single (lambda (node)
(if (pair? node) (cons new-name (cdr node))
node))))
(lambda (node context base-node)
(if (nodeset? node)
(map rename-single node)
(rename-single node)))))
(define modif:delete
(lambda (node context base-node) '()))
(define modif:delete-undeep
(let ((delete-undeep-single
(lambda (node)
(if (pair? node) (cdr node) '()))))
(lambda (node context base-node)
(if (nodeset? node)
(map delete-undeep-single node)
(delete-undeep-single node)))))
(define (sxml:modify . update-specifiers)
(and-let*
((lambdas-upd-specifiers
(sxml:update-specifiers->lambdas update-specifiers)))
(lambda (doc)
(sxml:transform-document
doc
(sxml:lambdas-upd-specifiers->targets doc lambdas-upd-specifiers)))))
(define (sxml:clone node)
(letrec
((clone-nodeset (lambda (nset)
(if (null? nset)
nset
(cons (sxml:clone (car nset)) (cdr nset))))))
(cond
((pair? node)
(cons (car node) (clone-nodeset (cdr node))))
((string? node)
(string-copy node))
((number? node)
(string->number (number->string node)))
(else node))))
(define (sxml:clone-nset-except nodeset node)
(letrec
((iter-nset
(lambda (nset encountered?)
(cond
((null? nset) nset)
((eq? (car nset) node)
(cons
(if encountered? (sxml:clone (car nset)) (car nset))
(iter-nset (cdr nset) #t)))
(else
(cons (sxml:clone (car nset))
(iter-nset (cdr nset) encountered?)))))))
(iter-nset nodeset #f)))
(define (sxml:replace-next-with-lst! prev lst)
(let ((next (cddr prev)))
(if
(null? lst) (set-cdr! prev next)
(begin
(set-cdr! prev lst)
(let loop ((lst lst)) (if
(null? (cdr lst))
(set-cdr! lst next)
(loop (cdr lst))))))))
(define (sxml:mutate-doc! doc mutation-lst)
(letrec
((tree-walk
(lambda (curr-node targets-alist)
(if
(not (pair? curr-node)) #t (let loop ((lst curr-node)
(targets targets-alist))
(if
(null? targets)
#t (begin
(if ((ntype?? '@) (car lst)) (tree-walk (car lst) targets-alist)
#t )
(if
(null? (cdr lst)) #t (let ((next (cadr lst)))
(call-with-values
(lambda ()
(sxml:separate-list
(lambda (pair) (eq? (caar pair) next))
targets))
(lambda (matched targets )
(if
(null? matched) (loop (cdr lst) targets)
(let ((matched
(map
(lambda (pair) (cons (cdar pair) (cdr pair)))
matched)))
(cond
((assv '() matched) => (lambda (pair)
(let ((k (length (cdr pair))))
(sxml:replace-next-with-lst! lst (cdr pair))
(loop (list-tail lst k) targets))))
(else
(tree-walk next matched)
(loop (cdr lst) targets))))))))))))))))
(let ((targets-alist
(map-union
(lambda (pair)
(let ((node-path (reverse (sxml:context->content (car pair)))))
(if
(eq? (car node-path) doc)
(list (cons (cdr node-path) (cdr pair)))
'())))
mutation-lst)))
(cond
((null? targets-alist) #t)
((assv '() targets-alist) => (lambda (pair)
(set! doc (cadr pair))))
(else
(tree-walk doc targets-alist)))
doc)))
(define (sxml:nodes-to-mutate doc update-targets)
(letrec
( (tree-walk
(lambda (curr-node targets-alist)
(call-with-values
(lambda () (sxml:separate-list
(lambda (pair) (null? (car pair)))
targets-alist))
(lambda (matched targets )
(if
(null? matched)
(let loop ((targets targets-alist)
(subnodes (append (sxml:attr-list curr-node)
((sxml:child sxml:node?) curr-node)))
(res '()))
(if
(or (null? targets) (null? subnodes))
res
(call-with-values
(lambda ()
(sxml:separate-list
(lambda (pair) (eq? (caar pair) (car subnodes)))
targets))
(lambda (matched targets)
(loop targets
(cdr subnodes)
(if
(null? matched)
res
(append res
(tree-walk
(car subnodes)
(map
(lambda (pair) (cons (cdar pair) (cdr pair)))
matched)))))))))
(list
(cons (cadar matched) (sxml:clone-nset-except
(as-nodeset
(sxml:tree-trans curr-node targets-alist))
curr-node)))))))))
(let ((targets-alist
(map-union
(lambda (triple)
(let ((node-path (reverse (sxml:context->content (car triple)))))
(if
(eq? (car node-path) doc)
(list (cons (cdr node-path) triple))
'())))
update-targets)))
(if (null? targets-alist) '()
(tree-walk doc targets-alist)))))
(define (sxml:modify! . update-specifiers)
(and-let*
((lambdas-upd-specifiers
(sxml:update-specifiers->lambdas update-specifiers)))
(lambda (doc)
(sxml:mutate-doc!
doc
(sxml:nodes-to-mutate
doc
(sxml:lambdas-upd-specifiers->targets doc lambdas-upd-specifiers))))))
(provide (all-defined)))