0
# DOM4J Advanced Features
1
2
This section covers DOM4J's advanced capabilities including JAXB integration, JavaBean binding, XML Schema datatype support, DTD processing, rule-based processing, Swing integration, and other specialized features for enterprise XML processing scenarios.
3
4
## JAXB Integration
5
6
DOM4J provides comprehensive integration with JAXB (Java Architecture for XML Binding) for object-to-XML mapping and vice versa.
7
8
### Package and Import
9
```java { .api }
10
import org.dom4j.jaxb.JAXBReader;
11
import org.dom4j.jaxb.JAXBWriter;
12
import org.dom4j.jaxb.JAXBSupport;
13
import org.dom4j.jaxb.JAXBObjectHandler;
14
import org.dom4j.jaxb.JAXBModifier;
15
import org.dom4j.jaxb.JAXBRuntimeException;
16
import javax.xml.bind.JAXBContext;
17
import javax.xml.bind.Unmarshaller;
18
import javax.xml.bind.Marshaller;
19
```
20
21
### JAXBReader - XML to Object Binding
22
```java { .api }
23
public class JAXBReader extends SAXReader {
24
// Constructors
25
public JAXBReader(String contextPath);
26
public JAXBReader(String contextPath, ClassLoader classLoader);
27
public JAXBReader(JAXBContext jaxbContext);
28
29
// Object handling
30
public void setObjectHandler(JAXBObjectHandler objectHandler);
31
public JAXBObjectHandler getObjectHandler();
32
33
// JAXB context
34
public JAXBContext getJAXBContext();
35
public Unmarshaller getUnmarshaller() throws JAXBException;
36
}
37
```
38
39
### Using JAXBReader
40
```java { .api }
41
// Define JAXB classes
42
@XmlRootElement(name = "book")
43
@XmlAccessorType(XmlAccessType.FIELD)
44
public class Book {
45
@XmlAttribute
46
private String isbn;
47
48
@XmlElement
49
private String title;
50
51
@XmlElement
52
private String author;
53
54
@XmlElement
55
private BigDecimal price;
56
57
// Constructors, getters, setters...
58
}
59
60
@XmlRootElement(name = "catalog")
61
@XmlAccessorType(XmlAccessType.FIELD)
62
public class Catalog {
63
@XmlElement(name = "book")
64
private List<Book> books = new ArrayList<>();
65
66
// Constructors, getters, setters...
67
}
68
69
// Create JAXBReader
70
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class, Book.class);
71
JAXBReader reader = new JAXBReader(jaxbContext);
72
73
// Object handler to process unmarshalled objects
74
reader.setObjectHandler(new JAXBObjectHandler() {
75
@Override
76
public void handleObject(Object object) throws Exception {
77
if (object instanceof Book) {
78
Book book = (Book) object;
79
System.out.println("Processed book: " + book.getTitle());
80
// Process book object
81
} else if (object instanceof Catalog) {
82
Catalog catalog = (Catalog) object;
83
System.out.println("Processed catalog with " + catalog.getBooks().size() + " books");
84
}
85
}
86
});
87
88
// Read and process XML
89
Document document = reader.read("catalog.xml");
90
// Objects are automatically unmarshalled and processed through handler
91
92
// Access unmarshalled objects from document
93
Catalog catalog = (Catalog) document.getRootElement().getData();
94
```
95
96
### JAXBWriter - Object to XML Binding
97
```java { .api }
98
public class JAXBWriter extends XMLWriter {
99
// Constructors
100
public JAXBWriter(String contextPath);
101
public JAXBWriter(String contextPath, ClassLoader classLoader);
102
public JAXBWriter(JAXBContext jaxbContext);
103
public JAXBWriter(String contextPath, Writer writer);
104
public JAXBWriter(JAXBContext jaxbContext, Writer writer, OutputFormat format);
105
106
// Object writing
107
public void writeObject(Object object) throws IOException, JAXBException;
108
public void writeCloseObject(Object object) throws IOException;
109
public void writeOpenObject(Object object) throws IOException;
110
111
// JAXB context
112
public JAXBContext getJAXBContext();
113
public Marshaller getMarshaller() throws JAXBException;
114
}
115
```
116
117
### Using JAXBWriter
118
```java { .api }
119
// Create objects to marshal
120
Catalog catalog = new Catalog();
121
catalog.getBooks().add(new Book("978-1234567890", "XML Processing", "John Doe", new BigDecimal("29.99")));
122
catalog.getBooks().add(new Book("978-0987654321", "JAXB Guide", "Jane Smith", new BigDecimal("34.99")));
123
124
// Create JAXBWriter
125
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class, Book.class);
126
127
try (FileWriter writer = new FileWriter("output.xml")) {
128
OutputFormat format = OutputFormat.createPrettyPrint();
129
JAXBWriter jaxbWriter = new JAXBWriter(jaxbContext, writer, format);
130
131
// Write object as XML
132
jaxbWriter.writeObject(catalog);
133
}
134
135
// Stream writing for large collections
136
try (FileWriter writer = new FileWriter("streaming.xml")) {
137
JAXBWriter jaxbWriter = new JAXBWriter(jaxbContext, writer);
138
139
// Manual document structure with object marshalling
140
jaxbWriter.writeOpen(new Catalog());
141
142
for (Book book : getAllBooks()) {
143
jaxbWriter.writeObject(book);
144
}
145
146
jaxbWriter.writeClose(new Catalog());
147
}
148
```
149
150
### JAXBModifier - Document Transformation with Objects
151
```java { .api }
152
public class JAXBModifier extends SAXModifier {
153
public JAXBModifier(String contextPath);
154
public JAXBModifier(JAXBContext jaxbContext);
155
156
public void addObjectHandler(String path, JAXBObjectHandler handler);
157
public void removeObjectHandler(String path);
158
}
159
```
160
161
### Using JAXBModifier
162
```java { .api }
163
// Modify documents using JAXB objects
164
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
165
JAXBModifier modifier = new JAXBModifier(jaxbContext);
166
167
// Add handler for specific elements
168
modifier.addObjectHandler("/catalog/book", new JAXBObjectHandler() {
169
@Override
170
public void handleObject(Object object) throws Exception {
171
if (object instanceof Book) {
172
Book book = (Book) object;
173
174
// Modify book object
175
if (book.getPrice().compareTo(new BigDecimal("50")) > 0) {
176
book.setPrice(book.getPrice().multiply(new BigDecimal("0.9"))); // 10% discount
177
}
178
179
// Return modified object
180
return book;
181
}
182
}
183
});
184
185
// Process document
186
Document modifiedDoc = modifier.modify(originalDocument);
187
```
188
189
## JavaBean Integration
190
191
DOM4J provides reflection-based XML binding through the bean package for automatic JavaBean to XML mapping.
192
193
### Package and Import
194
```java { .api }
195
import org.dom4j.bean.BeanElement;
196
import org.dom4j.bean.BeanAttribute;
197
import org.dom4j.bean.BeanDocumentFactory;
198
import org.dom4j.bean.BeanMetaData;
199
```
200
201
### BeanElement - JavaBean as XML Element
202
```java { .api }
203
public class BeanElement extends DefaultElement {
204
// Constructors
205
public BeanElement(String name, Object bean);
206
public BeanElement(QName qname, Object bean);
207
208
// Bean access
209
public Object getBean();
210
public void setBean(Object bean);
211
212
// Attribute mapping
213
public BeanMetaData getBeanMetaData();
214
}
215
```
216
217
### Using JavaBean Integration
218
```java { .api }
219
// JavaBean class
220
public class Product {
221
private String id;
222
private String name;
223
private String category;
224
private double price;
225
private boolean available;
226
227
// Standard getters and setters
228
public String getId() { return id; }
229
public void setId(String id) { this.id = id; }
230
231
public String getName() { return name; }
232
public void setName(String name) { this.name = name; }
233
234
public String getCategory() { return category; }
235
public void setCategory(String category) { this.category = category; }
236
237
public double getPrice() { return price; }
238
public void setPrice(double price) { this.price = price; }
239
240
public boolean isAvailable() { return available; }
241
public void setAvailable(boolean available) { this.available = available; }
242
}
243
244
// Create BeanElement from JavaBean
245
Product product = new Product();
246
product.setId("P123");
247
product.setName("XML Parser");
248
product.setCategory("Software");
249
product.setPrice(99.99);
250
product.setAvailable(true);
251
252
// Create document with bean elements
253
Document document = DocumentHelper.createDocument();
254
BeanElement productElement = new BeanElement("product", product);
255
document.setRootElement(productElement);
256
257
// Bean properties become XML attributes and elements automatically
258
String xml = document.asXML();
259
// Results in XML reflecting bean properties
260
261
// Read bean data from XML
262
String value = productElement.attributeValue("id"); // "P123"
263
boolean available = Boolean.parseBoolean(productElement.attributeValue("available")); // true
264
265
// Modify bean through XML
266
productElement.addAttribute("price", "89.99");
267
// This updates the underlying bean object
268
double newPrice = product.getPrice(); // 89.99
269
```
270
271
### BeanDocumentFactory - Bean-Aware Document Creation
272
```java { .api }
273
public class BeanDocumentFactory extends DocumentFactory {
274
public BeanDocumentFactory();
275
276
@Override
277
public Element createElement(QName qname);
278
279
@Override
280
public Attribute createAttribute(Element owner, QName qname, String value);
281
}
282
```
283
284
### Using BeanDocumentFactory
285
```java { .api }
286
// Use bean-aware factory
287
BeanDocumentFactory beanFactory = new BeanDocumentFactory();
288
SAXReader reader = new SAXReader();
289
reader.setDocumentFactory(beanFactory);
290
291
// Documents created with bean support
292
Document beanDocument = reader.read("products.xml");
293
294
// Elements automatically become BeanElements where appropriate
295
Element root = beanDocument.getRootElement();
296
if (root instanceof BeanElement) {
297
BeanElement beanRoot = (BeanElement) root;
298
Object bean = beanRoot.getBean();
299
// Work with underlying bean object
300
}
301
```
302
303
## XML Schema Datatype Support
304
305
DOM4J provides XML Schema datatype integration for type-safe XML processing through the datatype package.
306
307
### Package and Import
308
```java { .api }
309
import org.dom4j.datatype.DatatypeDocumentFactory;
310
import org.dom4j.datatype.DatatypeElement;
311
import org.dom4j.datatype.DatatypeAttribute;
312
import org.dom4j.datatype.DatatypeElementFactory;
313
import org.dom4j.datatype.InvalidSchemaException;
314
```
315
316
### DatatypeDocumentFactory - Schema-Aware Factory
317
```java { .api }
318
public class DatatypeDocumentFactory extends DocumentFactory {
319
// Constructors
320
public DatatypeDocumentFactory();
321
322
// Schema loading
323
public void loadSchema(Document schemaDocument) throws InvalidSchemaException;
324
public void loadSchema(Document schemaDocument, String uri) throws InvalidSchemaException;
325
326
// Datatype-aware creation
327
@Override
328
public Element createElement(QName qname);
329
330
@Override
331
public Attribute createAttribute(Element owner, QName qname, String value);
332
}
333
```
334
335
### Using Schema Datatype Support
336
```java { .api }
337
// Load XML Schema
338
Document schemaDoc = new SAXReader().read("product-schema.xsd");
339
340
// Create datatype-aware factory
341
DatatypeDocumentFactory datatypeFactory = new DatatypeDocumentFactory();
342
datatypeFactory.loadSchema(schemaDoc);
343
344
// Use with SAX reader
345
SAXReader reader = new SAXReader();
346
reader.setDocumentFactory(datatypeFactory);
347
348
// Parse document with datatype support
349
Document document = reader.read("products.xml");
350
351
// Elements have datatype information
352
Element priceElement = (Element) document.selectSingleNode("//price");
353
if (priceElement instanceof DatatypeElement) {
354
DatatypeElement dtPrice = (DatatypeElement) priceElement;
355
356
// Get typed data
357
Object typedValue = dtPrice.getData(); // Returns appropriate Java type
358
if (typedValue instanceof BigDecimal) {
359
BigDecimal price = (BigDecimal) typedValue;
360
System.out.println("Price: " + price);
361
}
362
363
// Schema validation
364
boolean valid = dtPrice.isValid();
365
if (!valid) {
366
String error = dtPrice.getValidationMessage();
367
System.err.println("Validation error: " + error);
368
}
369
}
370
371
// Attributes also have datatype support
372
Attribute idAttr = priceElement.attribute("id");
373
if (idAttr instanceof DatatypeAttribute) {
374
DatatypeAttribute dtId = (DatatypeAttribute) idAttr;
375
Object idValue = dtId.getData(); // Typed according to schema
376
}
377
```
378
379
### Custom Datatype Elements
380
```java { .api }
381
// Implement custom datatype element factory
382
public class CustomDatatypeElementFactory implements DatatypeElementFactory {
383
@Override
384
public Element createElement(QName qname, XSType xsType) {
385
if ("decimal".equals(xsType.getName())) {
386
return new CurrencyElement(qname, xsType);
387
}
388
return new DatatypeElement(qname, xsType);
389
}
390
}
391
392
// Custom currency element with business logic
393
class CurrencyElement extends DatatypeElement {
394
public CurrencyElement(QName qname, XSType xsType) {
395
super(qname, xsType);
396
}
397
398
@Override
399
public void setText(String text) {
400
// Custom validation and formatting for currency
401
try {
402
BigDecimal amount = new BigDecimal(text);
403
if (amount.scale() > 2) {
404
amount = amount.setScale(2, RoundingMode.HALF_UP);
405
}
406
super.setText(amount.toString());
407
} catch (NumberFormatException e) {
408
throw new IllegalArgumentException("Invalid currency format: " + text);
409
}
410
}
411
412
public BigDecimal getCurrencyValue() {
413
return (BigDecimal) getData();
414
}
415
}
416
417
// Use custom factory
418
DatatypeDocumentFactory factory = new DatatypeDocumentFactory();
419
factory.setDatatypeElementFactory(new CustomDatatypeElementFactory());
420
```
421
422
## Rule-Based Processing
423
424
DOM4J provides XSLT-style pattern matching and rule-based processing through the rule package.
425
426
### Package and Import
427
```java { .api }
428
import org.dom4j.rule.Pattern;
429
import org.dom4j.rule.Rule;
430
import org.dom4j.rule.RuleSet;
431
import org.dom4j.rule.Stylesheet;
432
import org.dom4j.rule.Action;
433
```
434
435
### Pattern Interface
436
```java { .api }
437
public interface Pattern extends NodeFilter {
438
// Pattern matching priority (higher = more specific)
439
double getPriority();
440
441
// Match mode support
442
String getMatchesNodeName();
443
444
// Union pattern support
445
Pattern[] getUnionPatterns();
446
447
// Pattern properties
448
short getMatchType();
449
String getMatchesNodeName();
450
}
451
```
452
453
### Rule and RuleSet
454
```java { .api }
455
public class Rule {
456
// Constructors
457
public Rule();
458
public Rule(Pattern pattern);
459
public Rule(Pattern pattern, Action action);
460
461
// Pattern and action
462
public Pattern getPattern();
463
public void setPattern(Pattern pattern);
464
public Action getAction();
465
public void setAction(Action action);
466
467
// Execution
468
public void fireRule(Node node) throws Exception;
469
}
470
471
public class RuleSet {
472
// Rule management
473
public void addRule(Rule rule);
474
public void removeRule(Rule rule);
475
public Rule getMatchingRule(Node node);
476
477
// Rule execution
478
public void fireRule(Node node) throws Exception;
479
}
480
```
481
482
### Using Rule-Based Processing
483
```java { .api }
484
// Define processing actions
485
Action bookAction = new Action() {
486
@Override
487
public void run(Node node) throws Exception {
488
Element book = (Element) node;
489
String title = book.elementText("title");
490
String author = book.elementText("author");
491
System.out.println("Processing book: " + title + " by " + author);
492
493
// Add processing timestamp
494
book.addAttribute("processed", Instant.now().toString());
495
}
496
};
497
498
Action priceAction = new Action() {
499
@Override
500
public void run(Node node) throws Exception {
501
Element priceElement = (Element) node;
502
String priceText = priceElement.getText();
503
504
try {
505
BigDecimal price = new BigDecimal(priceText);
506
// Format price to 2 decimal places
507
String formatted = price.setScale(2, RoundingMode.HALF_UP).toString();
508
priceElement.setText(formatted);
509
} catch (NumberFormatException e) {
510
System.err.println("Invalid price: " + priceText);
511
}
512
}
513
};
514
515
// Create patterns and rules
516
Pattern bookPattern = DocumentHelper.createPattern("book");
517
Pattern pricePattern = DocumentHelper.createPattern("book/price");
518
519
Rule bookRule = new Rule(bookPattern, bookAction);
520
Rule priceRule = new Rule(pricePattern, priceAction);
521
522
// Create rule set
523
RuleSet ruleSet = new RuleSet();
524
ruleSet.addRule(bookRule);
525
ruleSet.addRule(priceRule);
526
527
// Apply rules to document
528
Document document = loadDocument();
529
applyRules(document, ruleSet);
530
531
// Recursive rule application
532
private void applyRules(Node node, RuleSet ruleSet) throws Exception {
533
// Apply rule to current node
534
Rule rule = ruleSet.getMatchingRule(node);
535
if (rule != null) {
536
rule.fireRule(node);
537
}
538
539
// Apply rules to children
540
if (node instanceof Branch) {
541
Branch branch = (Branch) node;
542
for (int i = 0; i < branch.nodeCount(); i++) {
543
applyRules(branch.node(i), ruleSet);
544
}
545
}
546
}
547
```
548
549
### Stylesheet - Template Processing
550
```java { .api }
551
public class Stylesheet {
552
// Template management
553
public void addTemplate(Rule rule);
554
public void setModeName(String modeName);
555
public String getModeName();
556
557
// Processing modes
558
public void applyTemplates(List<Node> list) throws Exception;
559
public void applyTemplates(List<Node> list, String mode) throws Exception;
560
public void run(List<Node> list) throws Exception;
561
public void run(List<Node> list, String mode) throws Exception;
562
}
563
```
564
565
### Using Stylesheet Processing
566
```java { .api }
567
// Create stylesheet with templates
568
Stylesheet stylesheet = new Stylesheet();
569
570
// Template for catalog root
571
stylesheet.addTemplate(new Rule(
572
DocumentHelper.createPattern("catalog"),
573
new Action() {
574
@Override
575
public void run(Node node) throws Exception {
576
System.out.println("Processing catalog...");
577
Element catalog = (Element) node;
578
579
// Process children with different mode
580
List<Node> books = catalog.selectNodes("book");
581
stylesheet.applyTemplates(books, "summary");
582
}
583
}
584
));
585
586
// Template for books in summary mode
587
Rule summaryRule = new Rule(
588
DocumentHelper.createPattern("book"),
589
new Action() {
590
@Override
591
public void run(Node node) throws Exception {
592
Element book = (Element) node;
593
String title = book.elementText("title");
594
String isbn = book.attributeValue("isbn");
595
System.out.println("Book summary: " + title + " (ISBN: " + isbn + ")");
596
}
597
}
598
);
599
summaryRule.setMode("summary");
600
stylesheet.addTemplate(summaryRule);
601
602
// Apply stylesheet
603
Document document = loadCatalog();
604
List<Node> roots = Arrays.asList(document.getRootElement());
605
stylesheet.run(roots);
606
```
607
608
## DTD Processing and Validation
609
610
DOM4J provides support for DTD (Document Type Definition) processing and validation.
611
612
### Package and Import
613
```java { .api }
614
import org.dom4j.DocumentType;
615
import org.dom4j.dtd.*;
616
```
617
618
### DocumentType Interface
619
```java { .api }
620
public interface DocumentType extends Node {
621
// DTD properties
622
String getElementName();
623
void setElementName(String elementName);
624
String getPublicID();
625
void setPublicID(String publicID);
626
String getSystemID();
627
void setSystemID(String systemID);
628
629
// Internal subset
630
List getInternalDeclarations();
631
void setInternalDeclarations(List internalDeclarations);
632
633
// External subset
634
List getExternalDeclarations();
635
void setExternalDeclarations(List externalDeclarations);
636
}
637
```
638
639
### Using DTD Support
640
```java { .api }
641
// Create document with DTD
642
Document document = DocumentHelper.createDocument();
643
644
// Add DOCTYPE declaration
645
DocumentType docType = DocumentHelper.createDocType(
646
"catalog", // Root element name
647
"-//Example//DTD Catalog 1.0//EN", // Public ID
648
"http://example.com/dtd/catalog.dtd" // System ID
649
);
650
document.setDocType(docType);
651
652
// Parse document with DTD validation
653
SAXReader reader = new SAXReader(true); // Enable validation
654
655
// Custom entity resolver for DTD
656
reader.setEntityResolver(new EntityResolver() {
657
@Override
658
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
659
if (systemId.endsWith("catalog.dtd")) {
660
// Return local DTD copy
661
return new InputSource(new FileInputStream("local-catalog.dtd"));
662
}
663
return null; // Use default resolution
664
}
665
});
666
667
try {
668
Document validatedDoc = reader.read("catalog.xml");
669
System.out.println("Document is valid according to DTD");
670
} catch (DocumentException e) {
671
System.err.println("DTD validation failed: " + e.getMessage());
672
}
673
674
// Access DTD information
675
DocumentType docTypeInfo = document.getDocType();
676
if (docTypeInfo != null) {
677
String rootElement = docTypeInfo.getElementName();
678
String publicId = docTypeInfo.getPublicID();
679
String systemId = docTypeInfo.getSystemID();
680
681
System.out.printf("DTD: %s PUBLIC '%s' '%s'%n", rootElement, publicId, systemId);
682
}
683
```
684
685
## Swing Integration
686
687
DOM4J provides Swing integration for creating tree and table models from XML documents.
688
689
### Package and Import
690
```java { .api }
691
import org.dom4j.swing.DocumentTreeModel;
692
import org.dom4j.swing.XMLTableModel;
693
import org.dom4j.swing.XMLTableDefinition;
694
import javax.swing.*;
695
import javax.swing.tree.TreeModel;
696
import javax.swing.table.TableModel;
697
```
698
699
### DocumentTreeModel - XML Tree Visualization
700
```java { .api }
701
public class DocumentTreeModel implements TreeModel {
702
// Constructors
703
public DocumentTreeModel(Document document);
704
public DocumentTreeModel(Node rootNode);
705
706
// TreeModel implementation
707
@Override
708
public Object getRoot();
709
@Override
710
public Object getChild(Object parent, int index);
711
@Override
712
public int getChildCount(Object parent);
713
@Override
714
public boolean isLeaf(Object node);
715
@Override
716
public int getIndexOfChild(Object parent, Object child);
717
718
// Tree event support
719
@Override
720
public void addTreeModelListener(TreeModelListener listener);
721
@Override
722
public void removeTreeModelListener(TreeModelListener listener);
723
}
724
```
725
726
### Using DocumentTreeModel
727
```java { .api }
728
// Create JTree from XML document
729
Document document = loadXMLDocument();
730
DocumentTreeModel treeModel = new DocumentTreeModel(document);
731
732
JTree xmlTree = new JTree(treeModel);
733
734
// Customize tree rendering
735
xmlTree.setCellRenderer(new DefaultTreeCellRenderer() {
736
@Override
737
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
738
boolean expanded, boolean leaf, int row, boolean hasFocus) {
739
740
Component component = super.getTreeCellRendererComponent(
741
tree, value, selected, expanded, leaf, row, hasFocus);
742
743
if (value instanceof Element) {
744
Element element = (Element) value;
745
setText(element.getName());
746
setIcon(getElementIcon(element));
747
} else if (value instanceof Attribute) {
748
Attribute attr = (Attribute) value;
749
setText("@" + attr.getName() + " = " + attr.getValue());
750
setIcon(getAttributeIcon());
751
} else if (value instanceof Text) {
752
Text text = (Text) value;
753
setText("\"" + text.getText().trim() + "\"");
754
setIcon(getTextIcon());
755
}
756
757
return component;
758
}
759
});
760
761
// Add to Swing component
762
JScrollPane treeScrollPane = new JScrollPane(xmlTree);
763
frame.add(treeScrollPane, BorderLayout.CENTER);
764
```
765
766
### XMLTableModel - Tabular XML Data
767
```java { .api }
768
public class XMLTableModel implements TableModel {
769
// Constructors
770
public XMLTableModel(XMLTableDefinition definition, Document document);
771
public XMLTableModel(XMLTableDefinition definition, List<Node> rows);
772
773
// TableModel implementation
774
@Override
775
public int getRowCount();
776
@Override
777
public int getColumnCount();
778
@Override
779
public String getColumnName(int columnIndex);
780
@Override
781
public Class<?> getColumnClass(int columnIndex);
782
@Override
783
public boolean isCellEditable(int rowIndex, int columnIndex);
784
@Override
785
public Object getValueAt(int rowIndex, int columnIndex);
786
@Override
787
public void setValueAt(Object value, int rowIndex, int columnIndex);
788
789
// Table event support
790
@Override
791
public void addTableModelListener(TableModelListener listener);
792
@Override
793
public void removeTableModelListener(TableModelListener listener);
794
}
795
```
796
797
### XMLTableDefinition - Table Structure Definition
798
```java { .api }
799
public class XMLTableDefinition {
800
// Constructors
801
public XMLTableDefinition();
802
803
// Row definition
804
public void setRowExpression(String rowXPath);
805
public String getRowExpression();
806
807
// Column definition
808
public void addColumnDefinition(String columnName, String columnXPath);
809
public void addColumnDefinition(String columnName, String columnXPath, Class<?> columnClass);
810
811
// Column access
812
public int getColumnCount();
813
public String getColumnName(int index);
814
public String getColumnXPath(int index);
815
public Class<?> getColumnClass(int index);
816
}
817
```
818
819
### Using XMLTableModel
820
```java { .api }
821
// Define table structure
822
XMLTableDefinition tableDef = new XMLTableDefinition();
823
tableDef.setRowExpression("//book"); // Each book is a row
824
825
// Define columns
826
tableDef.addColumnDefinition("Title", "title/text()", String.class);
827
tableDef.addColumnDefinition("Author", "author/text()", String.class);
828
tableDef.addColumnDefinition("ISBN", "@isbn", String.class);
829
tableDef.addColumnDefinition("Price", "@price", Double.class);
830
tableDef.addColumnDefinition("Available", "@available", Boolean.class);
831
832
// Create table model
833
Document catalog = loadCatalog();
834
XMLTableModel tableModel = new XMLTableModel(tableDef, catalog);
835
836
// Create JTable
837
JTable xmlTable = new JTable(tableModel);
838
839
// Configure table
840
xmlTable.setAutoCreateRowSorter(true);
841
xmlTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
842
843
// Custom cell renderers
844
xmlTable.getColumnModel().getColumn(3).setCellRenderer(new DefaultTableCellRenderer() {
845
private final NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
846
847
@Override
848
public void setValue(Object value) {
849
if (value instanceof Number) {
850
setText(currencyFormat.format(((Number) value).doubleValue()));
851
} else {
852
setText(value != null ? value.toString() : "");
853
}
854
}
855
});
856
857
// Add to Swing interface
858
JScrollPane tableScrollPane = new JScrollPane(xmlTable);
859
frame.add(tableScrollPane, BorderLayout.CENTER);
860
861
// Handle selection changes
862
xmlTable.getSelectionModel().addListSelectionListener(e -> {
863
if (!e.getValueIsAdjusting()) {
864
int selectedRow = xmlTable.getSelectedRow();
865
if (selectedRow >= 0) {
866
String title = (String) tableModel.getValueAt(selectedRow, 0);
867
System.out.println("Selected book: " + title);
868
}
869
}
870
});
871
```
872
873
## Utility Classes and Helper Features
874
875
### Singleton Strategy Pattern
876
```java { .api }
877
import org.dom4j.util.SingletonStrategy;
878
import org.dom4j.util.SimpleSingleton;
879
880
// Custom singleton strategy
881
public class ThreadLocalSingletonStrategy<T> implements SingletonStrategy<T> {
882
private final ThreadLocal<T> threadLocal = new ThreadLocal<>();
883
private final Class<T> type;
884
885
public ThreadLocalSingletonStrategy(Class<T> type) {
886
this.type = type;
887
}
888
889
@Override
890
public T instance() {
891
T instance = threadLocal.get();
892
if (instance == null) {
893
try {
894
instance = type.newInstance();
895
threadLocal.set(instance);
896
} catch (Exception e) {
897
throw new RuntimeException("Cannot create instance", e);
898
}
899
}
900
return instance;
901
}
902
}
903
904
// Use with document factory
905
SingletonStrategy<DocumentFactory> strategy = new ThreadLocalSingletonStrategy<>(DocumentFactory.class);
906
DocumentFactory factory = strategy.instance();
907
```
908
909
### User Data Support
910
```java { .api }
911
import org.dom4j.util.UserDataElement;
912
import org.dom4j.util.UserDataAttribute;
913
import org.dom4j.util.UserDataDocumentFactory;
914
915
// Elements that can store arbitrary user data
916
UserDataDocumentFactory userDataFactory = new UserDataDocumentFactory();
917
Document document = userDataFactory.createDocument();
918
919
Element element = userDataFactory.createElement("product");
920
if (element instanceof UserDataElement) {
921
UserDataElement userElement = (UserDataElement) element;
922
923
// Store custom application data
924
userElement.setData("businessObject", productBusinessObject);
925
userElement.setData("validationState", ValidationState.VALID);
926
userElement.setData("lastModified", Instant.now());
927
928
// Retrieve user data
929
Product businessObj = (Product) userElement.getData("businessObject");
930
ValidationState state = (ValidationState) userElement.getData("validationState");
931
}
932
```
933
934
### Error Handling Utilities
935
```java { .api }
936
import org.dom4j.util.XMLErrorHandler;
937
938
// Comprehensive error handling
939
XMLErrorHandler errorHandler = new XMLErrorHandler();
940
941
// Configure error levels
942
errorHandler.setIgnoreWarnings(false);
943
errorHandler.setIgnoreErrors(false);
944
945
SAXReader reader = new SAXReader();
946
reader.setErrorHandler(errorHandler);
947
948
try {
949
Document document = reader.read("document.xml");
950
951
// Check for warnings and errors after parsing
952
if (errorHandler.hasErrors()) {
953
List<String> errors = errorHandler.getErrors();
954
System.err.println("Parsing errors occurred:");
955
errors.forEach(System.err::println);
956
}
957
958
if (errorHandler.hasWarnings()) {
959
List<String> warnings = errorHandler.getWarnings();
960
System.out.println("Parsing warnings:");
961
warnings.forEach(System.out::println);
962
}
963
964
} catch (DocumentException e) {
965
System.err.println("Fatal parsing error: " + e.getMessage());
966
}
967
```
968
969
### Node Comparison and Sorting
970
```java { .api }
971
import org.dom4j.util.NodeComparator;
972
import java.util.Comparator;
973
974
public class NodeComparator implements Comparator<Node> {
975
// Constructors
976
public NodeComparator();
977
978
// Comparison methods
979
public int compare(Node node1, Node node2);
980
981
// Comparison by different criteria
982
public static Comparator<Node> createDocumentOrderComparator();
983
public static Comparator<Node> createNameComparator();
984
public static Comparator<Node> createTextComparator();
985
}
986
```
987
988
**Usage Examples:**
989
990
```java
991
import org.dom4j.util.NodeComparator;
992
993
// Sort elements by name
994
List<Element> elements = document.selectNodes("//element");
995
elements.sort(NodeComparator.createNameComparator());
996
997
// Sort by document order
998
List<Node> nodes = document.selectNodes("//node()");
999
nodes.sort(new NodeComparator());
1000
1001
// Custom sorting
1002
elements.sort((e1, e2) -> {
1003
NodeComparator comparator = new NodeComparator();
1004
int nameCompare = e1.getName().compareTo(e2.getName());
1005
return nameCompare != 0 ? nameCompare : comparator.compare(e1, e2);
1006
});
1007
```
1008
1009
### Indexed Elements for Fast Access
1010
```java { .api }
1011
import org.dom4j.util.IndexedElement;
1012
import org.dom4j.util.IndexedDocumentFactory;
1013
1014
public class IndexedElement extends DefaultElement {
1015
// Constructor
1016
public IndexedElement(QName qname);
1017
public IndexedElement(String name);
1018
1019
// Indexed access methods
1020
public Element elementByID(String elementID);
1021
public List<Element> elementsByName(QName qname);
1022
public List<Element> elementsByName(String name);
1023
1024
// Index management
1025
public void indexElements();
1026
public boolean isIndexed();
1027
}
1028
1029
public class IndexedDocumentFactory extends DocumentFactory {
1030
// Singleton access
1031
public static IndexedDocumentFactory getInstance();
1032
1033
// Override factory methods to create indexed elements
1034
public Element createElement(QName qname);
1035
public Element createElement(String name);
1036
}
1037
```
1038
1039
**Usage Examples:**
1040
1041
```java
1042
// Create document with indexed elements
1043
IndexedDocumentFactory factory = IndexedDocumentFactory.getInstance();
1044
SAXReader reader = new SAXReader();
1045
reader.setDocumentFactory(factory);
1046
1047
Document document = reader.read("large-document.xml");
1048
Element root = document.getRootElement();
1049
1050
if (root instanceof IndexedElement) {
1051
IndexedElement indexedRoot = (IndexedElement) root;
1052
1053
// Fast element lookup by ID
1054
Element elementById = indexedRoot.elementByID("product-123");
1055
1056
// Fast lookup by name (faster than XPath for large documents)
1057
List<Element> products = indexedRoot.elementsByName("product");
1058
List<Element> categories = indexedRoot.elementsByName(QName.get("category", namespace));
1059
}
1060
```
1061
1062
## Performance Optimization Features
1063
1064
### Memory-Efficient Processing
1065
```java { .api }
1066
// Custom lightweight implementations
1067
public class LightweightDocumentFactory extends DocumentFactory {
1068
@Override
1069
public Element createElement(QName qname) {
1070
return new LightweightElement(qname);
1071
}
1072
1073
@Override
1074
public Document createDocument() {
1075
return new LightweightDocument();
1076
}
1077
}
1078
1079
// Optimized for memory usage
1080
class LightweightElement extends AbstractElement {
1081
private QName qname;
1082
private Object content; // Single object or List for multiple children
1083
private Object attributes; // Single attribute or Map for multiple
1084
1085
public LightweightElement(QName qname) {
1086
this.qname = qname;
1087
}
1088
1089
// Optimized implementations that use less memory
1090
// ... implementation details
1091
}
1092
```
1093
1094
### Caching and Performance
1095
```java { .api }
1096
// XPath compilation caching
1097
public class CachedXPathProcessor {
1098
private final Map<String, XPath> xpathCache = new ConcurrentHashMap<>();
1099
private final Map<String, String> namespaces;
1100
1101
public CachedXPathProcessor(Map<String, String> namespaces) {
1102
this.namespaces = namespaces != null ? Map.copyOf(namespaces) : Map.of();
1103
}
1104
1105
public XPath getOrCreateXPath(String expression) {
1106
return xpathCache.computeIfAbsent(expression, expr -> {
1107
try {
1108
XPath xpath = DocumentHelper.createXPath(expr);
1109
if (!namespaces.isEmpty()) {
1110
xpath.setNamespaceURIs(namespaces);
1111
}
1112
return xpath;
1113
} catch (InvalidXPathException e) {
1114
throw new RuntimeException("Invalid XPath: " + expr, e);
1115
}
1116
});
1117
}
1118
1119
// Batch processing methods
1120
public Map<String, List<Node>> executeQueries(Document document, List<String> expressions) {
1121
return expressions.parallelStream()
1122
.collect(Collectors.toConcurrentMap(
1123
expr -> expr,
1124
expr -> getOrCreateXPath(expr).selectNodes(document)
1125
));
1126
}
1127
}
1128
```
1129
1130
DOM4J's advanced features provide enterprise-grade capabilities for complex XML processing scenarios. From object binding and schema validation to rule-based processing and GUI integration, these features enable sophisticated XML applications while maintaining DOM4J's core principles of flexibility and ease of use.