#lang scribble/doc @(require scribble/manual "util.rkt" (for-label (this-package-in main))) @title[#:tag "sxslt"]{SXML Transformation} @defproc[(sxml:modify [updater _update-spec] ...) (-> _node _node)]{ Returns a procedure that applies the given @racket[updater]s to an SXML document, producing a new SXML document. Each @racket[updater] has the following form: @racketgrammar*[ #:literals (list quote) [update-spec (list xpath-location-path action action-param ...)] [action 'delete 'delete-undeep 'insert-into 'insert-following 'insert-preceding 'replace 'move-into 'move-following 'move-preceding handler-proc]] The @racket[xpath-location-path] describes the nodes to be transformed (see also @racket[sxpath]). The @racket[xpath-location-path] is interpreted with respect to some base node. If the location path is absolute, the base node is the root of the document being transformed. If the location path is relative, the base node is the node selected by the previous @racket[updater]. The following combinations of @racket[action]s and @racket[action-param]s are supported: @specsubform[#:literals (quote) 'delete]{ Deletes the selected nodes. } @specsubform[#:literals (quote) 'delete-undeep]{ Deletes the selected nodes, but keeps all of their contents, which thus move one level upwards in the document tree. } @specsubform[#:literals (quote) (code:line 'insert-into new-node ...)]{ Inserts the @racket[new-node]s as the last children of the selected nodes. } @specsubform[#:literals (quote) (code:line 'insert-following new-node ...)]{ Inserts the @racket[new-node]s after the selected nodes. } @specsubform[#:literals (quote) (code:line 'insert-preceding new-node ...)]{ Inserts the @racket[new-node]s before the selected nodes. } @specsubform[#:literals (quote) (code:line 'replace new-node ...)]{ Replaces the selected nodes with the @racket[new-node]s. } @specsubform[#:literals (quote) (code:line 'rename new-tag)]{ Renames the selected nodes, replacing its element tag with @racket[new-tag]. } @specsubform[#:literals (quote) (code:line 'move-into new-location-path)]{ Moves the selected nodes to a new location. The selected nodes become the last children of the nodes selected by @racket[new-location-path]. } @specsubform[#:literals (quote) (code:line 'move-following new-location-path)]{ Moves the selected nodes to a new location. The selected nodes are placed immediately after the nodes selected by @racket[new-location-path]. } @specsubform[#:literals (quote) (code:line 'move-preceding new-location-path)]{ Moves the selected nodes to a new location. The selected nodes are placed immediately before the nodes selected by @racket[new-location-path]. } @specsubform[handler-proc]{ Applies @racket[handler-proc] to three arguments: the selected node, a context (?), and the base node. The procedure must return a node or nodeset, which replaces the selected node. } @examples[#:eval the-eval (define sample-doc `(*TOP* (html (title "the title") (body (p "paragraph 1") (p "paragraph 2"))))) ((sxml:modify (list "//title" 'delete)) sample-doc) ((sxml:modify (list "//body" 'delete-undeep)) sample-doc) ((sxml:modify (list "//body" 'rename 'table) (list "p" (lambda (node ctx root) `(tr (td ,node))))) sample-doc) ] } @defproc[(pre-post-order [tree _node] [bindings (listof _binding)]) _node]{ Traverses @racket[tree], applying the transformation rules specified by @racket[bindings] to each node. @racketgrammar*[ #:literals (list* quote *preorder* *macro* *text* *default*) [binding (list* trigger-symbol '*preorder* . handler-proc) (list* trigger-symbol '*macro* . handler-proc) (list* trigger-symbol new-bindings . handler-proc) (list* trigger-symbol . handler-proc)] [trigger-symbol XMLname '*text* '*default*] ] The @racket[pre-post-order] function visits the nodes and nodelists pre-post-order (depth-first). For each node of the form @racket[(_name _node ...)] it looks up an association with the given @racket[_name] among its @racket[bindings]. If it fails, @racket[pre-post-order] tries to locate a default binding. It's an error if the latter attempt fails as well. The following types of @racket[_binding] are supported: @specsubform[#:literals (list* quote) (list* trigger-symbol '*preorder* . handler-proc)]{ The @racket[handler-proc] is applied to each node matching @racket[trigger-symbol] without first processing the node's contents. The result is not traversed by @racket[pre-post-order]. } @specsubform[#:literals (list* quote) (list* trigger-symbol '*macro* . handler-proc)]{ This is equivalent to @racket['*preorder*] described above. However, the result is re-processed again, with the current stylesheet. } @specsubform[#:literals (list* quote) (list* trigger-symbol new-bindings . handler-proc)] @specsubform[#:literals (list* quote) (list* trigger-symbol . handler-proc)]{ The @racket[handler-proc] is applied to each node matching @racket[trigger-symbol], but only after the node's contents have been recursively processed, using the additional @racket[_new-bindings], if present. To be more precise, the handler is applied to the head of the current node and its processed children. The result of the handler, which should also be a tree, replaces the current node. If the current node is a text string or other atom, a special binding with a symbol @racket[*text*] is looked up. } @examples[#:eval the-eval (define sample-doc `(*TOP* (html (title "the title") (body (p "paragraph 1") (p "paragraph 2"))))) (define italicizer `((p . ,(lambda (tag . content) (cons tag (cons "PARAGRAPH BEGINS: " content)))) (*text* . ,(lambda (tag content) `(i ,content))) (*default* . ,(lambda args args)))) (pre-post-order sample-doc italicizer) #| should produce '(*TOP* (html (title (i "the title")) (body (p "PARAGRAPH BEGINS: " (i "paragraph 1")) (p "PARAGRAPH BEGINS: " (i "paragraph 2"))))) |# ] }