package provide xotcl::xml::recreatorVisitor 1.0 package require -exact xotcl::xml::parser 1.0 package require XOTcl 1 namespace eval ::xotcl::xml::recreatorVisitor { namespace import ::xotcl::* ############################################################################## # # a visitor that recreates an XML representation from a # node tree # ############################################################################# Class XMLRecreatorVisitor -superclass NodeTreeVisitor -parameter useCDATA # # determine nesting depth of an object if the aggregation tree # XMLRecreatorVisitor instproc nestingDepth {obj} { for {set d 0;set s [$obj info parent]} {$s ne "::"} {set s [$s info parent]} { incr d } return $d } # # insert appropriate number of spaces for indentation -> return space string # XMLRecreatorVisitor instproc indent chars { set spaces " " for {set l 9} {$l<$chars} {incr l $l} {append spaces $spaces} return [string range $spaces 1 $chars] } XMLRecreatorVisitor instproc insertIndent {obj} { my instvar nestingStart return [my indent [expr {([my nestingDepth $obj] - $nestingStart)*2} - 2]] } XMLRecreatorVisitor instproc attrIndent {objName fa} { upvar [self callinglevel] $fa firstAttr if {$firstAttr} { set firstAttr 0 return " " } else { return "\n[my insertIndent $objName] " } } XMLRecreatorVisitor instproc getContent objName { return [$objName content] } XMLRecreatorVisitor instproc hasOnlyAttrs {obj} { if {[$obj exists pcdata]} {return 0} foreach c [$obj info children] { if {[$c istype XMLNode]} {return 0} } return 1 } # # hook to append line feed dependent on the object # default is to append one \n # XMLRecreatorVisitor instproc appendLineFeed obj { return "\n" } # # evaluate node objName # XMLRecreatorVisitor instproc visit objName { my instvar result set c [my getContent $objName] if {$c ne ""} { $objName instvar attributes pcdata set ns [$objName resolveNS] set firstAttr 1 set attrStr "" if {[string first $objName $ns] != -1} { # append xmlns attributes, xmlns=... first if {[$ns exists nsArray(xmlns)]} { append attrStr [my attrIndent $objName firstAttr] append attrStr "xmlns = \"[$ns set nsArray(xmlns)]\"" } foreach a [$ns array names nsArray] { if {$a ne "xmlns"} { append attrStr [my attrIndent $objName firstAttr] append attrStr "xmlns:${a} = \"[$ns set nsArray($a)]\"" } } } foreach a [array names attributes] { append attrStr [my attrIndent $objName firstAttr] append attrStr "$a = \"$attributes($a)\"" } append result "[my insertIndent $objName]<${c}$attrStr" if {[my hasOnlyAttrs $objName]} { append result "/>" } else { append result ">" } if {[info exists pcdata] && [llength $pcdata]>1 && [lindex $pcdata 0] eq ""} { append result " " [my pcdataString [lindex $pcdata 1]] } append result [my appendLineFeed $objName] } return $result } XMLRecreatorVisitor instproc pcdataString text { if {[my exists useCDATA] && [regexp < $text]} { return "" } return $text } # # evaluate end of a node # XMLRecreatorVisitor instproc visitEnd objName { my instvar result set c [$objName content] if {$c ne ""} { if {![my hasOnlyAttrs $objName]} { append result [my insertIndent $objName] \n } } # a child is responsible for the "mixed content" data elements # that have a location after the child set p [$objName info parent] if {[$p istype XMLElement] && [$p mixedContent]} { foreach {location data} [$p set pcdata] { if {$location == $objName} { append result [my insertIndent $objName] \ [my pcdataString $data] \n } } } } # # public method to be called on top node -> returns XML text as result # XMLRecreatorVisitor instproc interpretNodeTree node { my instvar result set result "" my set nestingStart [my nestingDepth $node] $node accept [self] return $result } namespace export XMLRecreatorVisitor } namespace import ::xotcl::xml::recreatorVisitor::*