File: xmlRecreatorVisitor.xotcl

package info (click to toggle)
xotcl 1.6.8-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 7,468 kB
  • sloc: ansic: 22,485; tcl: 2,531; sh: 791; makefile: 141
file content (159 lines) | stat: -rw-r--r-- 4,289 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

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 "<!\[CDATA\[$text]]>"
	}
	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] </$c>\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::*