0
# DOM Processing
1
2
Enhanced DOM processing with Groovy category methods providing GPath-style navigation and manipulation for standard W3C DOM objects. Enables idiomatic Groovy syntax for working with existing DOM documents.
3
4
## Capabilities
5
6
### DOMCategory
7
8
Category class that adds GPath-style operations to Java's DOM classes, allowing natural Groovy syntax for DOM navigation and manipulation.
9
10
#### Global Configuration
11
12
```groovy { .api }
13
/**
14
* Get global whitespace trimming setting
15
* @return true if whitespace is trimmed globally
16
*/
17
static synchronized boolean isGlobalTrimWhitespace()
18
19
/**
20
* Set global whitespace trimming for all DOM operations
21
* @param trimWhitespace - true to trim whitespace globally
22
*/
23
static synchronized void setGlobalTrimWhitespace(boolean trimWhitespace)
24
25
/**
26
* Get global ignorable whitespace preservation setting
27
* @return true if ignorable whitespace is preserved
28
*/
29
static synchronized boolean isGlobalKeepIgnorableWhitespace()
30
31
/**
32
* Set global ignorable whitespace preservation
33
* @param keepIgnorableWhitespace - true to preserve ignorable whitespace
34
*/
35
static synchronized void setGlobalKeepIgnorableWhitespace(boolean keepIgnorableWhitespace)
36
```
37
38
#### Navigation Methods
39
40
```groovy { .api }
41
/**
42
* Get child elements by name (GPath-style access)
43
* @param element - Parent Element
44
* @param elementName - Name of child elements to find
45
* @return Child elements or single element
46
*/
47
static Object get(Element element, String elementName)
48
49
/**
50
* Get elements from NodeList by name
51
* @param nodeList - NodeList to search
52
* @param elementName - Element name to find
53
* @return Matching elements
54
*/
55
static Object get(NodeList nodeList, String elementName)
56
57
/**
58
* Get attribute from NamedNodeMap
59
* @param nodeMap - NamedNodeMap to search
60
* @param elementName - Attribute name
61
* @return Attribute value
62
*/
63
static Object get(NamedNodeMap nodeMap, String elementName)
64
65
/**
66
* Get element attributes as NamedNodeMap
67
* @param element - Element to get attributes from
68
* @return NamedNodeMap of attributes
69
*/
70
static NamedNodeMap attributes(Element element)
71
72
/**
73
* Get node name
74
* @param node - Node to get name from
75
* @return Node name as string
76
*/
77
static String name(Node node)
78
79
/**
80
* Get parent node
81
* @param node - Child node
82
* @return Parent Node
83
*/
84
static Node parent(Node node)
85
```
86
87
#### Content Access Methods
88
89
```groovy { .api }
90
/**
91
* Get text content of node and all descendants
92
* @param node - Node to get text from
93
* @return Combined text content
94
*/
95
static String text(Node node)
96
97
/**
98
* Get text content from all nodes in NodeList
99
* @param nodeList - NodeList to get text from
100
* @return Combined text content
101
*/
102
static String text(NodeList nodeList)
103
104
/**
105
* Get direct text content of element (excludes descendants)
106
* @param element - Element to get local text from
107
* @return List of direct text content
108
*/
109
static List<String> localText(Element element)
110
111
/**
112
* Convert NodeList to List<Node>
113
* @param nodeList - NodeList to convert
114
* @return List of Node objects
115
*/
116
static List<Node> list(NodeList nodeList)
117
```
118
119
#### Tree Traversal Methods
120
121
```groovy { .api }
122
/**
123
* Depth-first traversal of element tree
124
* @param element - Root element for traversal
125
* @return NodeList with depth-first ordered nodes
126
*/
127
static NodeList depthFirst(Element element)
128
129
/**
130
* Breadth-first traversal of element tree
131
* @param element - Root element for traversal
132
* @return NodeList with breadth-first ordered nodes
133
*/
134
static NodeList breadthFirst(Element element)
135
136
/**
137
* Get immediate children of element
138
* @param element - Parent element
139
* @return NodeList of child elements
140
*/
141
static NodeList children(Element element)
142
```
143
144
#### Modification Methods
145
146
```groovy { .api }
147
/**
148
* Append new child element
149
* @param element - Parent element
150
* @param name - Name of new child element
151
* @return New child Element
152
*/
153
static Element appendNode(Element element, Object name)
154
155
/**
156
* Append child element with attributes
157
* @param element - Parent element
158
* @param name - Name of new child element
159
* @param attributes - Map of attributes
160
* @return New child Element
161
*/
162
static Element appendNode(Element element, Object name, Map attributes)
163
164
/**
165
* Append child element with text content
166
* @param element - Parent element
167
* @param name - Name of new child element
168
* @param value - Text content for new element
169
* @return New child Element
170
*/
171
static Element appendNode(Element element, Object name, String value)
172
173
/**
174
* Append child element with attributes and text content
175
* @param element - Parent element
176
* @param name - Name of new child element
177
* @param attributes - Map of attributes
178
* @param value - Text content
179
* @return New child Element
180
*/
181
static Element appendNode(Element element, Object name, Map attributes, String value)
182
183
/**
184
* Set text content of element
185
* @param element - Element to modify
186
* @param value - New text content
187
*/
188
static void setValue(Element element, String value)
189
190
/**
191
* Set element property/attribute value
192
* @param element - Element to modify
193
* @param property - Property/attribute name
194
* @param value - New value
195
*/
196
static void putAt(Element element, String property, Object value)
197
198
/**
199
* Add child nodes using closure
200
* @param element - Parent element
201
* @param closure - Closure defining new child content
202
*/
203
static void plus(Element element, Closure closure)
204
205
/**
206
* Add child nodes to NodeList using closure
207
* @param nodeList - NodeList to extend
208
* @param closure - Closure defining new content
209
*/
210
static void plus(NodeList nodeList, Closure closure)
211
```
212
213
#### Indexing Operations
214
215
```groovy { .api }
216
/**
217
* Get child node by index
218
* @param node - Parent node
219
* @param index - Child index
220
* @return Child Node at index
221
*/
222
static Node getAt(Node node, int index)
223
224
/**
225
* Get node range from parent
226
* @param node - Parent node
227
* @param range - IntRange of indices
228
* @return NodeList with nodes in range
229
*/
230
static NodeList getAt(Node node, IntRange range)
231
```
232
233
#### Utility Methods
234
235
```groovy { .api }
236
/**
237
* Get size of NamedNodeMap
238
* @param namedNodeMap - NamedNodeMap to measure
239
* @return Number of items
240
*/
241
static int size(NamedNodeMap namedNodeMap)
242
243
/**
244
* Get size of NodeList
245
* @param nodeList - NodeList to measure
246
* @return Number of nodes
247
*/
248
static int size(NodeList nodeList)
249
250
/**
251
* Check if NodeList is empty
252
* @param nodeList - NodeList to check
253
* @return true if empty
254
*/
255
static boolean isEmpty(NodeList nodeList)
256
```
257
258
#### XPath Support
259
260
```groovy { .api }
261
/**
262
* Evaluate XPath expression with specified return type
263
* @param node - Context node for XPath evaluation
264
* @param expression - XPath expression string
265
* @param returnType - javax.xml.namespace.QName specifying return type
266
* @return XPath evaluation result
267
*/
268
static Object xpath(Node node, String expression, javax.xml.namespace.QName returnType)
269
270
/**
271
* Evaluate XPath expression returning string result
272
* @param node - Context node for XPath evaluation
273
* @param expression - XPath expression string
274
* @return String result of XPath evaluation
275
*/
276
static String xpath(Node node, String expression)
277
```
278
279
#### Node Replacement Methods
280
281
```groovy { .api }
282
/**
283
* Replace node with content generated by closure
284
* @param self - Node to replace
285
* @param c - Closure generating replacement content
286
* @return New replacement Node
287
*/
288
static Node replaceNode(Node self, Closure c)
289
290
/**
291
* Replace nodes in NodesHolder with closure-generated content
292
* @param self - NodesHolder containing nodes to replace
293
* @param c - Closure generating replacement content
294
* @return New replacement Node
295
*/
296
static Node replaceNode(NodesHolder self, Closure c)
297
```
298
299
#### Enhanced Indexing Operations
300
301
```groovy { .api }
302
/**
303
* Get node from NodeListsHolder by index
304
* @param o - NodeListsHolder to access
305
* @param i - Index of node
306
* @return Node at index
307
*/
308
static Node getAt(NodeListsHolder o, int i)
309
310
/**
311
* Get node from NodesHolder by index
312
* @param o - NodesHolder to access
313
* @param i - Index of node
314
* @return Node at index
315
*/
316
static Node getAt(NodesHolder o, int i)
317
318
/**
319
* Get node range from NodeListsHolder
320
* @param o - NodeListsHolder to access
321
* @param r - IntRange of indices
322
* @return NodeList with nodes in range
323
*/
324
static NodeList getAt(NodeListsHolder o, IntRange r)
325
326
/**
327
* Get node range from NodesHolder
328
* @param o - NodesHolder to access
329
* @param r - IntRange of indices
330
* @return NodeList with nodes in range
331
*/
332
static NodeList getAt(NodesHolder o, IntRange r)
333
```
334
335
#### Internal Helper Classes
336
337
```groovy { .api }
338
/**
339
* Internal NodeList implementation for DOM operations
340
*/
341
private static final class NodeListsHolder implements NodeList
342
343
/**
344
* Internal Node collection for DOM operations
345
*/
346
private static final class NodesHolder implements NodeList
347
```
348
349
**Usage Examples:**
350
351
```groovy
352
import groovy.xml.DOMBuilder
353
import groovy.xml.dom.DOMCategory
354
import org.w3c.dom.*
355
356
// Create DOM document
357
def builder = DOMBuilder.newInstance()
358
Document doc = builder.library {
359
book(id: "1", isbn: "123-456") {
360
title("Groovy Programming")
361
author("John Doe")
362
price("49.99")
363
categories {
364
category("Programming")
365
category("Groovy")
366
}
367
}
368
book(id: "2", isbn: "789-012") {
369
title("XML Processing")
370
author("Jane Smith")
371
price("39.99")
372
categories {
373
category("XML")
374
category("Data Processing")
375
}
376
}
377
}
378
379
// Use DOMCategory for enhanced DOM operations
380
use(DOMCategory) {
381
Element root = doc.documentElement
382
383
// GPath-style navigation
384
println root.book.size() // 2
385
println root.book[0].title.text() // "Groovy Programming"
386
println root.book*.@id // ["1", "2"]
387
println root.book*.title*.text() // ["Groovy Programming", "XML Processing"]
388
389
// Attribute access
390
def firstBook = root.book[0]
391
println firstBook.@isbn // "123-456"
392
println firstBook.attributes().getNamedItem("id").value // "1"
393
394
// Text content access
395
println firstBook.author.text() // "John Doe"
396
println firstBook.categories.category*.text() // ["Programming", "Groovy"]
397
398
// Tree traversal
399
root.depthFirst().each { node ->
400
if (node.nodeType == Node.ELEMENT_NODE) {
401
println "Element: ${node.name()}"
402
}
403
}
404
405
// Modification operations
406
def newBook = root.appendNode("book", [id: "3", isbn: "345-678"])
407
newBook.appendNode("title", "Advanced Groovy")
408
newBook.appendNode("author", "Expert Author")
409
newBook.appendNode("price", "59.99")
410
411
// Set element content
412
def description = newBook.appendNode("description")
413
description.setValue("Comprehensive guide to advanced Groovy techniques")
414
415
// Add using closure
416
newBook + {
417
tags('new-release': 'true') {
418
tag('advanced')
419
tag('groovy')
420
}
421
}
422
423
// XPath operations
424
def expensiveBooks = root.xpath("//book[price > 45]", javax.xml.xpath.XPathConstants.NODESET)
425
println "Expensive books: ${expensiveBooks.length}"
426
427
def firstTitle = root.xpath("//book[1]/title/text()")
428
println "First title: ${firstTitle}"
429
430
// Global configuration
431
DOMCategory.setGlobalTrimWhitespace(true)
432
DOMCategory.setGlobalKeepIgnorableWhitespace(false)
433
434
// Collection operations
435
def allTitles = root.book.title.list()
436
allTitles.each { titleElement ->
437
println "Title element: ${titleElement.textContent}"
438
}
439
440
// Range operations
441
def firstTwoBooks = root.book[0..1]
442
println "First two books: ${firstTwoBooks.size()}"
443
444
// Parent navigation
445
def author = root.book[0].author
446
def book = author.parent()
447
println "Author's book ID: ${book.@id}"
448
449
// Local text (direct text content only)
450
def mixed = root.appendNode("mixed")
451
mixed.appendTextNode("Direct text")
452
mixed.appendNode("child", "Child text")
453
mixed.appendTextNode(" More direct text")
454
455
println mixed.localText() // ["Direct text", " More direct text"]
456
println mixed.text() // "Direct textChild text More direct text"
457
}
458
459
// Working with existing DOM from other sources
460
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
461
DocumentBuilder docBuilder = factory.newDocumentBuilder()
462
Document existingDoc = docBuilder.parse(new File("existing.xml"))
463
464
use(DOMCategory) {
465
// Apply same GPath operations to existing DOM
466
Element root = existingDoc.documentElement
467
println root.name()
468
469
// Find and modify elements
470
def targetElement = root.xpath("//target").item(0)
471
if (targetElement) {
472
targetElement.setValue("Updated content")
473
targetElement.putAt("modified", "true")
474
}
475
}
476
```