-
Notifications
You must be signed in to change notification settings - Fork 1
/
utils.tcl
508 lines (445 loc) · 15.4 KB
/
utils.tcl
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# utils.tcl - Copyright (C) 2001 Pat Thoyts <patthoyts@users.sourceforge.net>
#
# DOM data access utilities for use in the TclSOAP package.
#
# -------------------------------------------------------------------------
# This software is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the accompanying file `LICENSE'
# for more details.
# -------------------------------------------------------------------------
namespace eval ::SOAP {
namespace eval Utils {
variable version 1.0.1
variable rcsid {$Id: utils.tcl,v 1.9 2003/09/06 17:08:46 patthoyts Exp $}
namespace export getElements getElementsByName \
getElementValue getElementName \
getElementValues getElementNames \
getElementNamedValues \
getElementAttributes getElementAttribute \
decomposeSoap decomposeXMLRPC selectNode \
namespaceURI targetNamespaceURI \
nodeName baseElementName
}
}
# -------------------------------------------------------------------------
# Description:
# Provide a version independent selectNode implementation. We either use
# the version from the dom package or use the SOAP::xpath version if there
# is no dom one.
# Parameters:
# node - reference to a dom tree
# path - XPath selection
# Result:
# Returns the selected node or a list of matching nodes or an empty list
# if no match.
#
proc ::SOAP::Utils::selectNode {node path} {
package require SOAP::xpath
if {[catch {SOAP::xpath::xpath -node $node $path} r]} {
set r {}
}
return $r
}
# -------------------------------------------------------------------------
# for extracting the parameters from a SOAP packet.
# Arrays -> list
# Structs -> list of name/value pairs.
# a methods parameter list comes out looking like a struct where the member
# names == parameter names. This allows us to check the param name if we need
# to.
proc ::SOAP::Utils::is_array {domElement} {
# Look for "xsi:type"="SOAP-ENC:Array"
# FIX ME
# This code should check the namespace using namespaceURI code (CGI)
#
set attr [dom::node cget $domElement -attributes]
upvar #0 attr Attr
if {[info exists Attr(SOAP-ENC:arrayType)]} {
return 1
}
if {[info exists Attr(xsi:type)]} {
set type $Attr(xsi:type)
if {[string match -nocase {*:Array} $type]} {
return 1
}
}
# If all the child element names are the same, it's an array
# but of there is only one element???
set names [getElementNames $domElement]
if {[llength $names] > 1 && [llength [lsort -unique $names]] == 1} {
return 1
}
return 0
}
# -------------------------------------------------------------------------
# Break down a SOAP packet into a Tcl list of the data.
proc ::SOAP::Utils::decomposeSoap {domElement} {
set result {}
# get a list of the child elements of this base element.
set child_elements [getElements $domElement]
# if no child element - return the value.
if {$child_elements == {}} {
set result [getElementValue $domElement]
} else {
# decide if this is an array or struct
if {[is_array $domElement] == 1} {
foreach child $child_elements {
lappend result [decomposeSoap $child]
}
} else {
foreach child $child_elements {
lappend result [nodeName $child] [decomposeSoap $child]
}
}
}
return $result
}
# -------------------------------------------------------------------------
# I expect domElement to be the params element.
proc ::SOAP::Utils::decomposeXMLRPC {domElement} {
set result {}
foreach param_elt [getElements $domElement] {
lappend result [getXMLRPCValue [getElements $param_elt]]
}
return $result
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::getXMLRPCValue {value_elt} {
set value {}
if {$value_elt == {}} { return $value }
# if there is not type element then the specs say it's a string type.
set type_elt [getElements $value_elt]
if {$type_elt == {}} {
return [getElementValue $value_elt]
}
set type [getElementName $type_elt]
if {[string match "struct" $type]} {
foreach member_elt [getElements $type_elt] {
foreach elt [getElements $member_elt] {
set eltname [getElementName $elt]
if {[string match "name" $eltname]} {
set m_name [getElementValue $elt]
} elseif {[string match "value" $eltname]} {
set m_value [getXMLRPCValue $elt]
}
}
lappend value $m_name $m_value
}
} elseif {[string match "array" $type]} {
foreach elt [getElements [lindex [getElements $type_elt] 0]] {
lappend value [getXMLRPCValue $elt]
}
} else {
set value [getElementValue $type_elt]
}
return $value
}
# -------------------------------------------------------------------------
# Description:
# Return a list of all the immediate children of domNode that are element
# nodes.
# Parameters:
# domNode - a reference to a node in a dom tree
#
proc ::SOAP::Utils::getElements {domNode} {
set elements {}
if {$domNode != {}} {
foreach node [dom::node children $domNode] {
if {[dom::node cget $node -nodeType] == "element"} {
lappend elements $node
}
}
}
return $elements
}
# -------------------------------------------------------------------------
# Description:
# If there are child elements then recursively call this procedure on each
# child element. If this is a leaf element, then get the element value data.
# Parameters:
# domElement - a reference to a dom element node
# Result:
# Returns a value or a list of values.
#
proc ::SOAP::Utils::getElementValues {domElement} {
set result {}
if {$domElement != {}} {
set nodes [getElements $domElement]
if {$nodes =={}} {
set result [getElementValue $domElement]
} else {
foreach node $nodes {
lappend result [getElementValues $node]
}
}
}
return $result
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::getElementValuesList {domElement} {
set result {}
if {$domElement != {}} {
set nodes [getElements $domElement]
if {$nodes =={}} {
set result [getElementValue $domElement]
} else {
foreach node $nodes {
lappend result [getElementValues $node]
}
}
}
return $result
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::getElementNames {domElement} {
set result {}
if {$domElement != {}} {
set nodes [getElements $domElement]
if {$nodes == {}} {
set result [getElementName $domElement]
} else {
foreach node $nodes {
lappend result [getElementName $node]
}
}
}
return $result
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::getElementNamedValues {domElement} {
set name [getElementName $domElement]
set value {}
set nodes [getElements $domElement]
if {$nodes == {}} {
set value [getElementValue $domElement]
} else {
foreach node $nodes {
lappend value [getElementNamedValues $node]
}
}
return [list $name $value]
}
# -------------------------------------------------------------------------
# Description:
# Merge together all the child node values under a given dom element
# This procedure will also cope with elements whose data is elsewhere
# using the href attribute. We currently expect the data to be a local
# reference.
# Params:
# domElement - a reference to an element node in a dom tree
# Result:
# A string containing the elements value
#
proc ::SOAP::Utils::getElementValue {domElement} {
set r {}
set dataNodes [dom::node children $domElement]
if {[set href [href $domElement]] != {}} {
if {[string match "\#*" $href]} {
set href [string trimleft $href "\#"]
} else {
return -code error "cannot follow non-local href"
}
set r [[uplevel proc:name] [getNodeById \
[getDocumentElement $domElement] $href]]
}
foreach dataNode $dataNodes {
append r [dom::node cget $dataNode -nodeValue]
}
return $r
}
# -------------------------------------------------------------------------
# Description:
# Get the name of the current proc
# - from http://purl.org/thecliff/tcl/wiki/526.html
proc ::SOAP::Utils::proc:name {} {
lindex [info level -1] 0
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::href {node} {
set a [dom::node cget $node -attributes]
upvar #0 $a A
if {[info exists A(href)]} {
return $A(href)
}
return {}
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::id {node} {
set a [dom::node cget $node -attributes]
upvar #0 $a A
if {[info exists A(id)]} {
return $A(id)
}
return {}
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::getElementName {domElement} {
return [dom::node cget $domElement -nodeName]
}
# -------------------------------------------------------------------------
proc ::SOAP::Utils::getElementAttributes {domElement} {
set attr [dom::node cget $domElement -attributes]
set attrlist [array get $attr]
return $attrlist
}
# -------------------------------------------------------------------------
# Find a node by id (sort of the xpath id() function)
proc ::SOAP::Utils::getNodeById {base id} {
if {[string match $id [id $base]]} {
return $base
}
set r {}
set children [dom::node children $base]
foreach child $children {
set r [getNodeById $child $id]
if {$r != {}} { return $r }
}
return {}
}
# -------------------------------------------------------------------------
# Walk up the DOM until you get to the top.
proc ::SOAP::Utils::getDocumentElement {node} {
set parent [dom::node parent $node]
if {$parent == {}} {
return $node
} else {
return [getDocumentElement $parent]
}
}
# -------------------------------------------------------------------------
# Return the value of the specified atribute. First check for an exact match,
# if that fails look for an attribute name without any namespace specification.
# Result:
# Returns the value of the attribute.
#
proc ::SOAP::Utils::getElementAttribute {node attrname} {
set r {}
set attrs [array get [dom::node cget $node -attributes]]
if {[set ndx [lsearch -exact $attrs $attrname]] == -1} {
set ndx [lsearch -regexp $attrs ":${attrname}\$"]
}
if {$ndx != -1} {
incr ndx
set r [lindex $attrs $ndx]
}
return $r
}
# -------------------------------------------------------------------------
# Description:
# Get the namespace of the given node. This code will examine the nodes
# attributes and if necessary the parent nodes attributes until it finds
# a relevant namespace declaration.
# Parameters:
# node - the node for which to return a namespace
# Result:
# returns either the namespace uri or an empty string.
# Notes:
# The TclDOM 2.0 package provides a -namespaceURI option. The C code module
# does not, so we have the second chunk of code.
# The hasFeature method doesn't seem to provide information about this
# but the versions that support 'query' seem to have the namespaceURI
# method so we'll use this test for now.
#
proc ::SOAP::Utils::namespaceURI {node} {
#if {[dom::DOMImplementation hasFeature query 1.0]} {
# return [dom::node cget $node -namespaceURI]
#}
if {[catch {dom::node cget $node -namespaceURI} result]} {
set nodeName [dom::node cget $node -nodeName]
set ndx [string last : $nodeName]
set nodeNS [string range $nodeName 0 $ndx]
set nodeNS [string trimright $nodeNS :]
set result [find_namespaceURI $node $nodeNS]
}
return $result
}
# Description:
# As for namespaceURI except that we are interested in the targetNamespace
# URI. This is commonly used in XML schemas to specify the default namespace
# for the defined items.
#
proc ::SOAP::Utils::targetNamespaceURI {node value} {
set ndx [string last : $value]
set ns [string trimright [string range $value 0 $ndx] :]
#set base [string trimleft [string range $value $ndx end] :]
return [find_namespaceURI $node $ns 1]
}
# -------------------------------------------------------------------------
# Description:
# Obtain the unqualified part of a node name.
# Parameters:
# node - a DOM node
# Result:
# the node name without any namespace prefix.
#
proc ::SOAP::Utils::nodeName {node} {
set nodeName [dom::node cget $node -nodeName]
set nodeName [string range $nodeName [string last : $nodeName] end]
return [string trimleft $nodeName :]
}
proc ::SOAP::Utils::baseElementName {nodeName} {
set nodeName [string range $nodeName [string last : $nodeName] end]
return [string trimleft $nodeName :]
}
# -------------------------------------------------------------------------
# Description:
# Obtain the uri for the nsname namespace name working up the DOM tree
# from the given node.
# Parameters:
# node - the starting point in the tree.
# nsname - the namespace name. May be an null string.
# Result:
# Returns the namespace uri or an empty string.
#
proc ::SOAP::Utils::find_namespaceURI {node nsname {find_targetNamespace 0}} {
if {$node == {}} { return {} }
set atts [dom::node cget $node -attributes]
upvar #0 atts Atts
# check for the default namespace or targetNamespace
if {$nsname == {}} {
if {$find_targetNamespace} {
if {[info exists Atts(targetNamespace)]} {
return $Atts(targetNamespace)
}
} else {
if {[info exists Atts(xmlns)]} {
return $Atts(xmlns)
}
}
} else {
# check the defined namespace names.
foreach {attname attvalue} [array get $atts] {
if {[string match "xmlns:$nsname" $attname]} {
return $attvalue
}
}
}
# recurse through the parents.
return [find_namespaceURI [dom::node parent $node] $nsname $find_targetNamespace]
}
# -------------------------------------------------------------------------
# Description:
# Return a list of all the immediate children of domNode that are element
# nodes.
# Parameters:
# domNode - a reference to a node in a dom tree
#
proc ::SOAP::Utils::getElementsByName {domNode name} {
set elements {}
if {$domNode != {}} {
foreach node [dom::node children $domNode] {
if {[dom::node cget $node -nodeType] == "element"
&& [string match $name [dom::node cget $node -nodeName]]} {
lappend elements $node
}
}
}
return $elements
}
# -------------------------------------------------------------------------
package provide SOAP::Utils $::SOAP::Utils::version
# -------------------------------------------------------------------------
# Local variables:
# indent-tabs-mode: nil
# End: