-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathxco-util.xqm
338 lines (315 loc) · 12.6 KB
/
xco-util.xqm
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
module namespace u="http://www.parsqube.de/ns/xco/util";
import module namespace cf="http://www.parsqube.de/ns/xco/comp-finder"
at "xco-comp-finder.xqm";
import module namespace co="http://www.parsqube.de/ns/xco/constants"
at "xco-constants.xqm";
import module namespace ns="http://www.parsqube.de/ns/xco/namespace"
at "xco-namespace.xqm";
import module namespace sf="http://www.parsqube.de/ns/xco/string-filter"
at "xco-sfilter.xqm";
declare namespace z="http://www.parsqube.de/ns/xco/structure";
(:~
: Transforms a literal string into an equivalent regular
: expression. Rules:
: (1) string "%20" is replaced with \s
: (2) string "." is replaced with \s
: (3) wildcards "?" and "*" are retained
:
: @param literal a literal string
: @return the equivalent regular expression
:)
declare function u:literalToRegex($literal as xs:string)
as xs:string {
$literal
! replace(., '\.', '\\.')
! replace(., '[(){}\[\]]', '\\$0')
! replace(., '%20', '\\s')
};
(:~
: Resolves and normalizes a URI. The URI is resolved against
: an explicit base URI, or against the current work directory,
: if no explicit base URI is specified.
:)
declare function u:normalizeUri($uri as xs:string, $base as xs:string?)
as xs:string {
let $base := ($base, file:current-dir())[1]
return
file:resolve-path($uri, $base) ! replace(., '\\', '/')
! replace(., '/$', '')
! replace(., 'file:/*(([^/]:)?/.*)', '$1')
};
declare function u:removeAnno($node as node()) {
typeswitch($node)
case document-node() return document {$node/node() ! u:removeAnno(.)}
case element(xs:annotation) | element(z:annotation) return ()
case element() return
element {node-name($node)} {
$node/@* ! u:removeAnno(.),
ns:getNamespaceNodes($node),
$node/node() ! u:removeAnno(.)
}
case text() return
$node[not((preceding-sibling::*[1], following-sibling::*[1])
/self::*:annotation)]
case attribute() return $node
default return $node
};
(:~
: Removes from the deep content of an input node all attributes (1) in a
: namespace matching an optional namespace filter and (2) with a local name
: not matching an optional positive filter or matching an optional negative
: filter.
:)
declare function u:removeAtts($node as node(),
$nsFilter as xs:string?,
$keepFilter as xs:string?,
$discardFilter as xs:string?)
as node() {
let $nsFilterElem := $nsFilter ! sf:compileStringFilter(.)
let $keepFilterElem := $keepFilter ! sf:compileStringFilter(.)
let $discardFilterElem := $discardFilter ! sf:compileStringFilter(.)
return u:removeAttsREC($node, $nsFilterElem, $keepFilterElem, $discardFilterElem)
};
(:~
: Removes from the deep content of an input node all attributes in the z
: namespace with a local name not matching an optional positive filter or
: matching an optional negative filter.
:)
declare function u:removeZAtts($node as node(),
$keepFilter as xs:string?,
$discardFilter as xs:string?)
as node() {
let $nsFilterElem :=
sf:compileStringFilter('http://www.parsqube.de/ns/xco/structure http://www.w3.org/XML/1998/namespace')
let $keepFilterElem := $keepFilter ! sf:compileStringFilter(.)
let $discardFilterElem := $discardFilter ! sf:compileStringFilter(.)
return u:removeAttsREC($node, $nsFilterElem, $keepFilterElem, $discardFilterElem)
};
(:~
: Removes from the deep content of an input node all attributes (1)
: in a namespace matching an optional namespace filter and (2) with
: a local name not matching an optional positive filter or matching
: an optional negative filter.
:)
declare function u:removeAttsREC($node as node(),
$nsFilter as element()?,
$keepFilter as element()?,
$discardFilter as element()?)
as node()? {
typeswitch($node)
case document-node() return document {$node/node() !
u:removeAttsREC(., $nsFilter, $keepFilter, $discardFilter)}
case element() return
element {node-name($node)} {
$node/@* ! u:removeAttsREC(., $nsFilter, $keepFilter, $discardFilter),
$node/node() ! u:removeAttsREC(., $nsFilter, $keepFilter, $discardFilter)
}
case attribute() return
let $ns := namespace-uri($node)
return
if ($nsFilter and not(sf:matchesStringFilter($ns, $nsFilter))) then $node
else
let $cond := (
$keepFilter ! sf:matchesStringFilter(local-name($node), .),
$discardFilter ! not(sf:matchesStringFilter(local-name($node), .))
)
return
$node[every $c in $cond satisfies $cond]
default return $node
};
declare function u:prettyNode($node as node()) {
typeswitch($node)
case document-node() return document {$node/node() ! u:prettyNode(.)}
case element() return
element {node-name($node)} {
$node/@*,
ns:getNamespaceNodes($node),
(: [$node/not(deep-equal(in-scope-prefixes(.), parent::*/in-scope-prefixes(.)))], :)
$node/node() ! u:prettyNode(.)
}
case attribute() return $node
case text() return
$node[not(../*) or matches(., '\S')]
default return $node
};
declare function u:copyNode($node as node()) {
typeswitch($node)
case document-node() return document {$node/node()}
case element() return
element {node-name($node)} {
in-scope-prefixes($node)
! namespace {.} {namespace-uri-for-prefix(., $node)},
$node/(@*, node())
}
default return $node
};
(:~
: Modifies a set of schemas, adding to anonymous type definitions
: a @z:typeID attribute.
:)
declare function u:addLocalTypeIds($schemas as element(xs:schema)*)
as element(xs:schema)* {
for $schema at $snr in $schemas/root()
return
copy $schema_ := $schema
modify
let $atypes := $schema_//(xs:simpleType, xs:complexType)[not(@name)]
where $atypes
return (
for $atype at $tnr in $atypes return
insert node attribute z:typeID {'typedef-'||$snr||'.'||$tnr} into $atype,
insert node attribute xml:base {$schema/base-uri(.)} into $schema_/*
)
return $schema_/*
};
(:~
: Returns the type category for a given type name.
:)
declare function u:typeCategory($typeName as xs:QName, $schemas as element(xs:schema)*)
as xs:string? {
if (ns:isQNameBuiltin($typeName)) then
if (local-name-from-QName($typeName) = ('any', 'anyAttribute')) then 'at' else 'sb'
else
let $typeDef := cf:findType($typeName, $schemas)
let $typeCat := $typeDef[1] ! u:typeCategory(.)
return $typeCat[1]
};
declare function u:typeCategory($type as element())
as xs:string {
if ($type/self::xs:simpleType) then
if ($type/xs:restriction) then
if (empty($type/xs:restriction/(* except xs:annotation))
and $type/xs:restriction/@base/resolve-QName(., ..)
! ns:isQNameBuiltin(.)) then 'se'
else 'sr'
else if ($type/xs:list) then 'sl'
else if ($type/xs:union) then 'su'
else 's?'
else
if ($type/xs:complexContent) then 'cc'
else if ($type/xs:simpleContent) then 'cs'
else
let $children :=
$type/(., xs:complexContent/(xs:extension, xs:restriction))/
(xs:sequence, xs:choice, xs:all, xs:group)
let $atts :=
$type/(., xs:complexContent/(xs:extension, xs:restriction))/
(xs:attribute, xs:attributeGroup)
return
if ($children) then 'cc'
else if ($atts) then 'ca'
else 'ce'
};
declare function u:isTypeCategorySimple($typeCategory as xs:string?)
as xs:boolean {
$typeCategory ! starts-with(., 's')
};
(:~
: Maps a type definition to the simple type which it contains.
:)
declare function u:extractSimpleType($typeDef as element()?,
$schemas as element(xs:schema)*)
as item()? {
if (not($typeDef)) then () else
typeswitch($typeDef)
case element(xs:simpleType) return $typeDef
case element(xs:complexType) return
let $extension := $typeDef/xs:simpleContent/xs:extension
where $extension
return
let $localType := $extension/(xs:simpleType, xs:complexType)
return
if ($localType) then $localType/u:extractSimpleType(., $schemas)
else
let $base := $extension/@base/resolve-QName(., ..)
return
if (ns:isQNameBuiltin($base)) then $base else
$base ! cf:findType(., $schemas)
! u:extractSimpleType(., $schemas)
default return ()
};
(:~
: Maps an integer number to a letter representing it (1=a, 2=b, ...).
:)
declare function u:letterNumber($number as xs:integer)
as xs:string {
substring('abcdefghijklmnopqrstuvwxyz', $number, 1)
};
(:~
: Returns a string describing the location of a local type definition.
:)
declare function u:getLocalTypeLocator($type as element(),
$nsmap as element(z:nsMap),
$options as map(xs:string, item()*)?)
as xs:string? {
if (not($type/@z:typeID)) then () else
let $globalComp := $type/ancestor::*[parent::xs:schema]
let $globalCompName := $globalComp/@name/resolve-QName(., ..) ! ns:normalizeQName(., $nsmap)
let $globalCompKind := $globalComp/local-name()
let $withinCompPath :=
$type/(ancestor::xs:element, ancestor::xs:attribute)
[. >> $globalComp]/
@name/concat(parent::attribute()/'@', .) => string-join('/')
return
string-join((
$globalCompKind||'['||$globalCompName||']',
$withinCompPath[string()]
), '/')||'#'||$type/local-name(.)
};
(:~
: Returns a label identifying a local type definition.
:)
declare function u:getLocalTypeLabel($type as element(),
$nsmap as element(z:nsMap),
$options as map(xs:string, item()*)?)
as xs:string? {
u:getLocalTypeLocator($type, $nsmap, $options)
! concat(., ' (', $type/@z:typeID, ')')
};
(:~
: Returns the type label of a type descriptor.
:)
declare function u:getDescriptorLocalTypeLabel($desc as element())
as xs:string? {
$desc/@z:typeID ! concat(../@z:typeLocator, ' (', .,')')
};
(:~
: Returns the type label of a type descriptor as a couple of
: <div> elements. (Useful for display in two lines.)
:)
declare function u:getDescriptorLocalTypeLabelDivs($desc as xs:string)
as element(div)+ {
let $suffix := $desc ! replace(., '.*\s*(\(typedef.*)', '$1')[not(. eq $desc)]
let $prefix := $desc ! replace(., '\s*\(.*', '')
return (
<div><code>{$prefix}</code></div>,
$suffix ! <div><code>{.}</code></div>
)
};
(:~
: Writes a document to the file system. If the folder does not
: yet exist, it is created now.
:
: If no serialization parameters are specified, the output document
: is indented.
:
: @param path the file system path of the output file
: @param doc the documednt to be written (document or element node)
: @serParams optional serialization parameters
: @return the empty sequence
:)
declare function u:writeXmlDoc($path as xs:string,
$doc as node(),
$serParams as map(xs:string, item()*)?)
as empty-sequence() {
let $dir := $path ! replace(., '/[^/]*$', '')
let $_CRDIR := if (file:exists($dir)) then () else file:create-dir($dir)
let $spar := if (exists($serParams)) then $serParams else map{'indent': 'yes'}
return
file:write($path, $doc, $spar)
};
declare function u:writeXmlDoc($path as xs:string,
$doc as node())
as empty-sequence() {
u:writeXmlDoc($path, $doc, ())
};