CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-dom4j--dom4j

Flexible XML framework for Java providing comprehensive XML processing capabilities

Pending
Overview
Eval results
Files

advanced-features.mddocs/

DOM4J Advanced Features

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.

JAXB Integration

DOM4J provides comprehensive integration with JAXB (Java Architecture for XML Binding) for object-to-XML mapping and vice versa.

Package and Import

import org.dom4j.jaxb.JAXBReader;
import org.dom4j.jaxb.JAXBWriter;
import org.dom4j.jaxb.JAXBSupport;
import org.dom4j.jaxb.JAXBObjectHandler;
import org.dom4j.jaxb.JAXBModifier;
import org.dom4j.jaxb.JAXBRuntimeException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Marshaller;

JAXBReader - XML to Object Binding

public class JAXBReader extends SAXReader {
    // Constructors
    public JAXBReader(String contextPath);
    public JAXBReader(String contextPath, ClassLoader classLoader);
    public JAXBReader(JAXBContext jaxbContext);
    
    // Object handling
    public void setObjectHandler(JAXBObjectHandler objectHandler);
    public JAXBObjectHandler getObjectHandler();
    
    // JAXB context
    public JAXBContext getJAXBContext();
    public Unmarshaller getUnmarshaller() throws JAXBException;
}

Using JAXBReader

// Define JAXB classes
@XmlRootElement(name = "book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {
    @XmlAttribute
    private String isbn;
    
    @XmlElement
    private String title;
    
    @XmlElement
    private String author;
    
    @XmlElement
    private BigDecimal price;
    
    // Constructors, getters, setters...
}

@XmlRootElement(name = "catalog")
@XmlAccessorType(XmlAccessType.FIELD)
public class Catalog {
    @XmlElement(name = "book")
    private List<Book> books = new ArrayList<>();
    
    // Constructors, getters, setters...
}

// Create JAXBReader
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class, Book.class);
JAXBReader reader = new JAXBReader(jaxbContext);

// Object handler to process unmarshalled objects
reader.setObjectHandler(new JAXBObjectHandler() {
    @Override
    public void handleObject(Object object) throws Exception {
        if (object instanceof Book) {
            Book book = (Book) object;
            System.out.println("Processed book: " + book.getTitle());
            // Process book object
        } else if (object instanceof Catalog) {
            Catalog catalog = (Catalog) object;
            System.out.println("Processed catalog with " + catalog.getBooks().size() + " books");
        }
    }
});

// Read and process XML
Document document = reader.read("catalog.xml");
// Objects are automatically unmarshalled and processed through handler

// Access unmarshalled objects from document
Catalog catalog = (Catalog) document.getRootElement().getData();

JAXBWriter - Object to XML Binding

public class JAXBWriter extends XMLWriter {
    // Constructors
    public JAXBWriter(String contextPath);
    public JAXBWriter(String contextPath, ClassLoader classLoader);
    public JAXBWriter(JAXBContext jaxbContext);
    public JAXBWriter(String contextPath, Writer writer);
    public JAXBWriter(JAXBContext jaxbContext, Writer writer, OutputFormat format);
    
    // Object writing
    public void writeObject(Object object) throws IOException, JAXBException;
    public void writeCloseObject(Object object) throws IOException;
    public void writeOpenObject(Object object) throws IOException;
    
    // JAXB context
    public JAXBContext getJAXBContext();
    public Marshaller getMarshaller() throws JAXBException;
}

Using JAXBWriter

// Create objects to marshal
Catalog catalog = new Catalog();
catalog.getBooks().add(new Book("978-1234567890", "XML Processing", "John Doe", new BigDecimal("29.99")));
catalog.getBooks().add(new Book("978-0987654321", "JAXB Guide", "Jane Smith", new BigDecimal("34.99")));

// Create JAXBWriter
JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class, Book.class);

try (FileWriter writer = new FileWriter("output.xml")) {
    OutputFormat format = OutputFormat.createPrettyPrint();
    JAXBWriter jaxbWriter = new JAXBWriter(jaxbContext, writer, format);
    
    // Write object as XML
    jaxbWriter.writeObject(catalog);
}

// Stream writing for large collections
try (FileWriter writer = new FileWriter("streaming.xml")) {
    JAXBWriter jaxbWriter = new JAXBWriter(jaxbContext, writer);
    
    // Manual document structure with object marshalling
    jaxbWriter.writeOpen(new Catalog());
    
    for (Book book : getAllBooks()) {
        jaxbWriter.writeObject(book);
    }
    
    jaxbWriter.writeClose(new Catalog());
}

JAXBModifier - Document Transformation with Objects

public class JAXBModifier extends SAXModifier {
    public JAXBModifier(String contextPath);
    public JAXBModifier(JAXBContext jaxbContext);
    
    public void addObjectHandler(String path, JAXBObjectHandler handler);
    public void removeObjectHandler(String path);
}

Using JAXBModifier

// Modify documents using JAXB objects
JAXBContext jaxbContext = JAXBContext.newInstance(Book.class);
JAXBModifier modifier = new JAXBModifier(jaxbContext);

// Add handler for specific elements
modifier.addObjectHandler("/catalog/book", new JAXBObjectHandler() {
    @Override
    public void handleObject(Object object) throws Exception {
        if (object instanceof Book) {
            Book book = (Book) object;
            
            // Modify book object
            if (book.getPrice().compareTo(new BigDecimal("50")) > 0) {
                book.setPrice(book.getPrice().multiply(new BigDecimal("0.9"))); // 10% discount
            }
            
            // Return modified object
            return book;
        }
    }
});

// Process document
Document modifiedDoc = modifier.modify(originalDocument);

JavaBean Integration

DOM4J provides reflection-based XML binding through the bean package for automatic JavaBean to XML mapping.

Package and Import

import org.dom4j.bean.BeanElement;
import org.dom4j.bean.BeanAttribute;
import org.dom4j.bean.BeanDocumentFactory;
import org.dom4j.bean.BeanMetaData;

BeanElement - JavaBean as XML Element

public class BeanElement extends DefaultElement {
    // Constructors
    public BeanElement(String name, Object bean);
    public BeanElement(QName qname, Object bean);
    
    // Bean access
    public Object getBean();
    public void setBean(Object bean);
    
    // Attribute mapping
    public BeanMetaData getBeanMetaData();
}

Using JavaBean Integration

// JavaBean class
public class Product {
    private String id;
    private String name;
    private String category;
    private double price;
    private boolean available;
    
    // Standard getters and setters
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getCategory() { return category; }
    public void setCategory(String category) { this.category = category; }
    
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
    
    public boolean isAvailable() { return available; }
    public void setAvailable(boolean available) { this.available = available; }
}

// Create BeanElement from JavaBean
Product product = new Product();
product.setId("P123");
product.setName("XML Parser");
product.setCategory("Software");
product.setPrice(99.99);
product.setAvailable(true);

// Create document with bean elements
Document document = DocumentHelper.createDocument();
BeanElement productElement = new BeanElement("product", product);
document.setRootElement(productElement);

// Bean properties become XML attributes and elements automatically
String xml = document.asXML();
// Results in XML reflecting bean properties

// Read bean data from XML
String value = productElement.attributeValue("id"); // "P123"
boolean available = Boolean.parseBoolean(productElement.attributeValue("available")); // true

// Modify bean through XML
productElement.addAttribute("price", "89.99");
// This updates the underlying bean object
double newPrice = product.getPrice(); // 89.99

BeanDocumentFactory - Bean-Aware Document Creation

public class BeanDocumentFactory extends DocumentFactory {
    public BeanDocumentFactory();
    
    @Override
    public Element createElement(QName qname);
    
    @Override
    public Attribute createAttribute(Element owner, QName qname, String value);
}

Using BeanDocumentFactory

// Use bean-aware factory
BeanDocumentFactory beanFactory = new BeanDocumentFactory();
SAXReader reader = new SAXReader();
reader.setDocumentFactory(beanFactory);

// Documents created with bean support
Document beanDocument = reader.read("products.xml");

// Elements automatically become BeanElements where appropriate
Element root = beanDocument.getRootElement();
if (root instanceof BeanElement) {
    BeanElement beanRoot = (BeanElement) root;
    Object bean = beanRoot.getBean();
    // Work with underlying bean object
}

XML Schema Datatype Support

DOM4J provides XML Schema datatype integration for type-safe XML processing through the datatype package.

Package and Import

import org.dom4j.datatype.DatatypeDocumentFactory;
import org.dom4j.datatype.DatatypeElement;
import org.dom4j.datatype.DatatypeAttribute;
import org.dom4j.datatype.DatatypeElementFactory;
import org.dom4j.datatype.InvalidSchemaException;

DatatypeDocumentFactory - Schema-Aware Factory

public class DatatypeDocumentFactory extends DocumentFactory {
    // Constructors
    public DatatypeDocumentFactory();
    
    // Schema loading
    public void loadSchema(Document schemaDocument) throws InvalidSchemaException;
    public void loadSchema(Document schemaDocument, String uri) throws InvalidSchemaException;
    
    // Datatype-aware creation
    @Override
    public Element createElement(QName qname);
    
    @Override  
    public Attribute createAttribute(Element owner, QName qname, String value);
}

Using Schema Datatype Support

// Load XML Schema
Document schemaDoc = new SAXReader().read("product-schema.xsd");

// Create datatype-aware factory
DatatypeDocumentFactory datatypeFactory = new DatatypeDocumentFactory();
datatypeFactory.loadSchema(schemaDoc);

// Use with SAX reader
SAXReader reader = new SAXReader();
reader.setDocumentFactory(datatypeFactory);

// Parse document with datatype support
Document document = reader.read("products.xml");

// Elements have datatype information
Element priceElement = (Element) document.selectSingleNode("//price");
if (priceElement instanceof DatatypeElement) {
    DatatypeElement dtPrice = (DatatypeElement) priceElement;
    
    // Get typed data
    Object typedValue = dtPrice.getData(); // Returns appropriate Java type
    if (typedValue instanceof BigDecimal) {
        BigDecimal price = (BigDecimal) typedValue;
        System.out.println("Price: " + price);
    }
    
    // Schema validation
    boolean valid = dtPrice.isValid();
    if (!valid) {
        String error = dtPrice.getValidationMessage();
        System.err.println("Validation error: " + error);
    }
}

// Attributes also have datatype support
Attribute idAttr = priceElement.attribute("id");
if (idAttr instanceof DatatypeAttribute) {
    DatatypeAttribute dtId = (DatatypeAttribute) idAttr;
    Object idValue = dtId.getData(); // Typed according to schema
}

Custom Datatype Elements

// Implement custom datatype element factory
public class CustomDatatypeElementFactory implements DatatypeElementFactory {
    @Override
    public Element createElement(QName qname, XSType xsType) {
        if ("decimal".equals(xsType.getName())) {
            return new CurrencyElement(qname, xsType);
        }
        return new DatatypeElement(qname, xsType);
    }
}

// Custom currency element with business logic
class CurrencyElement extends DatatypeElement {
    public CurrencyElement(QName qname, XSType xsType) {
        super(qname, xsType);
    }
    
    @Override
    public void setText(String text) {
        // Custom validation and formatting for currency
        try {
            BigDecimal amount = new BigDecimal(text);
            if (amount.scale() > 2) {
                amount = amount.setScale(2, RoundingMode.HALF_UP);
            }
            super.setText(amount.toString());
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Invalid currency format: " + text);
        }
    }
    
    public BigDecimal getCurrencyValue() {
        return (BigDecimal) getData();
    }
}

// Use custom factory
DatatypeDocumentFactory factory = new DatatypeDocumentFactory();
factory.setDatatypeElementFactory(new CustomDatatypeElementFactory());

Rule-Based Processing

DOM4J provides XSLT-style pattern matching and rule-based processing through the rule package.

Package and Import

import org.dom4j.rule.Pattern;
import org.dom4j.rule.Rule;
import org.dom4j.rule.RuleSet;
import org.dom4j.rule.Stylesheet;
import org.dom4j.rule.Action;

Pattern Interface

public interface Pattern extends NodeFilter {
    // Pattern matching priority (higher = more specific)
    double getPriority();
    
    // Match mode support
    String getMatchesNodeName();
    
    // Union pattern support  
    Pattern[] getUnionPatterns();
    
    // Pattern properties
    short getMatchType();
    String getMatchesNodeName();
}

Rule and RuleSet

public class Rule {
    // Constructors
    public Rule();
    public Rule(Pattern pattern);
    public Rule(Pattern pattern, Action action);
    
    // Pattern and action
    public Pattern getPattern();
    public void setPattern(Pattern pattern);
    public Action getAction();
    public void setAction(Action action);
    
    // Execution
    public void fireRule(Node node) throws Exception;
}

public class RuleSet {
    // Rule management
    public void addRule(Rule rule);
    public void removeRule(Rule rule);
    public Rule getMatchingRule(Node node);
    
    // Rule execution
    public void fireRule(Node node) throws Exception;
}

Using Rule-Based Processing

// Define processing actions
Action bookAction = new Action() {
    @Override
    public void run(Node node) throws Exception {
        Element book = (Element) node;
        String title = book.elementText("title");
        String author = book.elementText("author");
        System.out.println("Processing book: " + title + " by " + author);
        
        // Add processing timestamp
        book.addAttribute("processed", Instant.now().toString());
    }
};

Action priceAction = new Action() {
    @Override
    public void run(Node node) throws Exception {
        Element priceElement = (Element) node;
        String priceText = priceElement.getText();
        
        try {
            BigDecimal price = new BigDecimal(priceText);
            // Format price to 2 decimal places
            String formatted = price.setScale(2, RoundingMode.HALF_UP).toString();
            priceElement.setText(formatted);
        } catch (NumberFormatException e) {
            System.err.println("Invalid price: " + priceText);
        }
    }
};

// Create patterns and rules
Pattern bookPattern = DocumentHelper.createPattern("book");
Pattern pricePattern = DocumentHelper.createPattern("book/price");

Rule bookRule = new Rule(bookPattern, bookAction);
Rule priceRule = new Rule(pricePattern, priceAction);

// Create rule set
RuleSet ruleSet = new RuleSet();
ruleSet.addRule(bookRule);
ruleSet.addRule(priceRule);

// Apply rules to document
Document document = loadDocument();
applyRules(document, ruleSet);

// Recursive rule application
private void applyRules(Node node, RuleSet ruleSet) throws Exception {
    // Apply rule to current node
    Rule rule = ruleSet.getMatchingRule(node);
    if (rule != null) {
        rule.fireRule(node);
    }
    
    // Apply rules to children
    if (node instanceof Branch) {
        Branch branch = (Branch) node;
        for (int i = 0; i < branch.nodeCount(); i++) {
            applyRules(branch.node(i), ruleSet);
        }
    }
}

Stylesheet - Template Processing

public class Stylesheet {
    // Template management
    public void addTemplate(Rule rule);
    public void setModeName(String modeName);
    public String getModeName();
    
    // Processing modes
    public void applyTemplates(List<Node> list) throws Exception;
    public void applyTemplates(List<Node> list, String mode) throws Exception;
    public void run(List<Node> list) throws Exception;
    public void run(List<Node> list, String mode) throws Exception;
}

Using Stylesheet Processing

// Create stylesheet with templates
Stylesheet stylesheet = new Stylesheet();

// Template for catalog root
stylesheet.addTemplate(new Rule(
    DocumentHelper.createPattern("catalog"),
    new Action() {
        @Override
        public void run(Node node) throws Exception {
            System.out.println("Processing catalog...");
            Element catalog = (Element) node;
            
            // Process children with different mode
            List<Node> books = catalog.selectNodes("book");
            stylesheet.applyTemplates(books, "summary");
        }
    }
));

// Template for books in summary mode
Rule summaryRule = new Rule(
    DocumentHelper.createPattern("book"),
    new Action() {
        @Override
        public void run(Node node) throws Exception {
            Element book = (Element) node;
            String title = book.elementText("title");
            String isbn = book.attributeValue("isbn");
            System.out.println("Book summary: " + title + " (ISBN: " + isbn + ")");
        }
    }
);
summaryRule.setMode("summary");
stylesheet.addTemplate(summaryRule);

// Apply stylesheet
Document document = loadCatalog();
List<Node> roots = Arrays.asList(document.getRootElement());
stylesheet.run(roots);

DTD Processing and Validation

DOM4J provides support for DTD (Document Type Definition) processing and validation.

Package and Import

import org.dom4j.DocumentType;
import org.dom4j.dtd.*;

DocumentType Interface

public interface DocumentType extends Node {
    // DTD properties  
    String getElementName();
    void setElementName(String elementName);
    String getPublicID();
    void setPublicID(String publicID);
    String getSystemID();
    void setSystemID(String systemID);
    
    // Internal subset
    List getInternalDeclarations();
    void setInternalDeclarations(List internalDeclarations);
    
    // External subset
    List getExternalDeclarations();
    void setExternalDeclarations(List externalDeclarations);
}

Using DTD Support

// Create document with DTD
Document document = DocumentHelper.createDocument();

// Add DOCTYPE declaration
DocumentType docType = DocumentHelper.createDocType(
    "catalog",                                    // Root element name
    "-//Example//DTD Catalog 1.0//EN",          // Public ID
    "http://example.com/dtd/catalog.dtd"         // System ID
);
document.setDocType(docType);

// Parse document with DTD validation
SAXReader reader = new SAXReader(true); // Enable validation

// Custom entity resolver for DTD
reader.setEntityResolver(new EntityResolver() {
    @Override
    public InputSource resolveEntity(String publicId, String systemId) throws IOException {
        if (systemId.endsWith("catalog.dtd")) {
            // Return local DTD copy
            return new InputSource(new FileInputStream("local-catalog.dtd"));
        }
        return null; // Use default resolution
    }
});

try {
    Document validatedDoc = reader.read("catalog.xml");
    System.out.println("Document is valid according to DTD");
} catch (DocumentException e) {
    System.err.println("DTD validation failed: " + e.getMessage());
}

// Access DTD information
DocumentType docTypeInfo = document.getDocType();
if (docTypeInfo != null) {
    String rootElement = docTypeInfo.getElementName();
    String publicId = docTypeInfo.getPublicID();
    String systemId = docTypeInfo.getSystemID();
    
    System.out.printf("DTD: %s PUBLIC '%s' '%s'%n", rootElement, publicId, systemId);
}

Swing Integration

DOM4J provides Swing integration for creating tree and table models from XML documents.

Package and Import

import org.dom4j.swing.DocumentTreeModel;
import org.dom4j.swing.XMLTableModel;
import org.dom4j.swing.XMLTableDefinition;
import javax.swing.*;
import javax.swing.tree.TreeModel;
import javax.swing.table.TableModel;

DocumentTreeModel - XML Tree Visualization

public class DocumentTreeModel implements TreeModel {
    // Constructors
    public DocumentTreeModel(Document document);
    public DocumentTreeModel(Node rootNode);
    
    // TreeModel implementation
    @Override
    public Object getRoot();
    @Override
    public Object getChild(Object parent, int index);
    @Override
    public int getChildCount(Object parent);
    @Override
    public boolean isLeaf(Object node);
    @Override
    public int getIndexOfChild(Object parent, Object child);
    
    // Tree event support
    @Override
    public void addTreeModelListener(TreeModelListener listener);
    @Override
    public void removeTreeModelListener(TreeModelListener listener);
}

Using DocumentTreeModel

// Create JTree from XML document
Document document = loadXMLDocument();
DocumentTreeModel treeModel = new DocumentTreeModel(document);

JTree xmlTree = new JTree(treeModel);

// Customize tree rendering
xmlTree.setCellRenderer(new DefaultTreeCellRenderer() {
    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
            boolean expanded, boolean leaf, int row, boolean hasFocus) {
        
        Component component = super.getTreeCellRendererComponent(
            tree, value, selected, expanded, leaf, row, hasFocus);
        
        if (value instanceof Element) {
            Element element = (Element) value;
            setText(element.getName());
            setIcon(getElementIcon(element));
        } else if (value instanceof Attribute) {
            Attribute attr = (Attribute) value;
            setText("@" + attr.getName() + " = " + attr.getValue());
            setIcon(getAttributeIcon());
        } else if (value instanceof Text) {
            Text text = (Text) value;
            setText("\"" + text.getText().trim() + "\"");
            setIcon(getTextIcon());
        }
        
        return component;
    }
});

// Add to Swing component
JScrollPane treeScrollPane = new JScrollPane(xmlTree);
frame.add(treeScrollPane, BorderLayout.CENTER);

XMLTableModel - Tabular XML Data

public class XMLTableModel implements TableModel {
    // Constructors
    public XMLTableModel(XMLTableDefinition definition, Document document);
    public XMLTableModel(XMLTableDefinition definition, List<Node> rows);
    
    // TableModel implementation
    @Override
    public int getRowCount();
    @Override
    public int getColumnCount();
    @Override
    public String getColumnName(int columnIndex);
    @Override
    public Class<?> getColumnClass(int columnIndex);
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex);
    @Override
    public Object getValueAt(int rowIndex, int columnIndex);
    @Override
    public void setValueAt(Object value, int rowIndex, int columnIndex);
    
    // Table event support
    @Override
    public void addTableModelListener(TableModelListener listener);
    @Override
    public void removeTableModelListener(TableModelListener listener);
}

XMLTableDefinition - Table Structure Definition

public class XMLTableDefinition {
    // Constructors
    public XMLTableDefinition();
    
    // Row definition
    public void setRowExpression(String rowXPath);
    public String getRowExpression();
    
    // Column definition
    public void addColumnDefinition(String columnName, String columnXPath);
    public void addColumnDefinition(String columnName, String columnXPath, Class<?> columnClass);
    
    // Column access
    public int getColumnCount();
    public String getColumnName(int index);
    public String getColumnXPath(int index);
    public Class<?> getColumnClass(int index);
}

Using XMLTableModel

// Define table structure
XMLTableDefinition tableDef = new XMLTableDefinition();
tableDef.setRowExpression("//book"); // Each book is a row

// Define columns
tableDef.addColumnDefinition("Title", "title/text()", String.class);
tableDef.addColumnDefinition("Author", "author/text()", String.class);
tableDef.addColumnDefinition("ISBN", "@isbn", String.class);
tableDef.addColumnDefinition("Price", "@price", Double.class);
tableDef.addColumnDefinition("Available", "@available", Boolean.class);

// Create table model
Document catalog = loadCatalog();
XMLTableModel tableModel = new XMLTableModel(tableDef, catalog);

// Create JTable
JTable xmlTable = new JTable(tableModel);

// Configure table
xmlTable.setAutoCreateRowSorter(true);
xmlTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

// Custom cell renderers
xmlTable.getColumnModel().getColumn(3).setCellRenderer(new DefaultTableCellRenderer() {
    private final NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();
    
    @Override
    public void setValue(Object value) {
        if (value instanceof Number) {
            setText(currencyFormat.format(((Number) value).doubleValue()));
        } else {
            setText(value != null ? value.toString() : "");
        }
    }
});

// Add to Swing interface
JScrollPane tableScrollPane = new JScrollPane(xmlTable);
frame.add(tableScrollPane, BorderLayout.CENTER);

// Handle selection changes
xmlTable.getSelectionModel().addListSelectionListener(e -> {
    if (!e.getValueIsAdjusting()) {
        int selectedRow = xmlTable.getSelectedRow();
        if (selectedRow >= 0) {
            String title = (String) tableModel.getValueAt(selectedRow, 0);
            System.out.println("Selected book: " + title);
        }
    }
});

Utility Classes and Helper Features

Singleton Strategy Pattern

import org.dom4j.util.SingletonStrategy;
import org.dom4j.util.SimpleSingleton;

// Custom singleton strategy
public class ThreadLocalSingletonStrategy<T> implements SingletonStrategy<T> {
    private final ThreadLocal<T> threadLocal = new ThreadLocal<>();
    private final Class<T> type;
    
    public ThreadLocalSingletonStrategy(Class<T> type) {
        this.type = type;
    }
    
    @Override
    public T instance() {
        T instance = threadLocal.get();
        if (instance == null) {
            try {
                instance = type.newInstance();
                threadLocal.set(instance);
            } catch (Exception e) {
                throw new RuntimeException("Cannot create instance", e);
            }
        }
        return instance;
    }
}

// Use with document factory
SingletonStrategy<DocumentFactory> strategy = new ThreadLocalSingletonStrategy<>(DocumentFactory.class);
DocumentFactory factory = strategy.instance();

User Data Support

import org.dom4j.util.UserDataElement;
import org.dom4j.util.UserDataAttribute;
import org.dom4j.util.UserDataDocumentFactory;

// Elements that can store arbitrary user data
UserDataDocumentFactory userDataFactory = new UserDataDocumentFactory();
Document document = userDataFactory.createDocument();

Element element = userDataFactory.createElement("product");
if (element instanceof UserDataElement) {
    UserDataElement userElement = (UserDataElement) element;
    
    // Store custom application data
    userElement.setData("businessObject", productBusinessObject);
    userElement.setData("validationState", ValidationState.VALID);
    userElement.setData("lastModified", Instant.now());
    
    // Retrieve user data
    Product businessObj = (Product) userElement.getData("businessObject");
    ValidationState state = (ValidationState) userElement.getData("validationState");
}

Error Handling Utilities

import org.dom4j.util.XMLErrorHandler;

// Comprehensive error handling
XMLErrorHandler errorHandler = new XMLErrorHandler();

// Configure error levels
errorHandler.setIgnoreWarnings(false);
errorHandler.setIgnoreErrors(false);

SAXReader reader = new SAXReader();
reader.setErrorHandler(errorHandler);

try {
    Document document = reader.read("document.xml");
    
    // Check for warnings and errors after parsing
    if (errorHandler.hasErrors()) {
        List<String> errors = errorHandler.getErrors();
        System.err.println("Parsing errors occurred:");
        errors.forEach(System.err::println);
    }
    
    if (errorHandler.hasWarnings()) {
        List<String> warnings = errorHandler.getWarnings();
        System.out.println("Parsing warnings:");
        warnings.forEach(System.out::println);
    }
    
} catch (DocumentException e) {
    System.err.println("Fatal parsing error: " + e.getMessage());
}

Node Comparison and Sorting

import org.dom4j.util.NodeComparator;
import java.util.Comparator;

public class NodeComparator implements Comparator<Node> {
    // Constructors
    public NodeComparator();
    
    // Comparison methods
    public int compare(Node node1, Node node2);
    
    // Comparison by different criteria
    public static Comparator<Node> createDocumentOrderComparator();
    public static Comparator<Node> createNameComparator();
    public static Comparator<Node> createTextComparator();
}

Usage Examples:

import org.dom4j.util.NodeComparator;

// Sort elements by name
List<Element> elements = document.selectNodes("//element");
elements.sort(NodeComparator.createNameComparator());

// Sort by document order
List<Node> nodes = document.selectNodes("//node()");
nodes.sort(new NodeComparator());

// Custom sorting
elements.sort((e1, e2) -> {
    NodeComparator comparator = new NodeComparator();
    int nameCompare = e1.getName().compareTo(e2.getName());
    return nameCompare != 0 ? nameCompare : comparator.compare(e1, e2);
});

Indexed Elements for Fast Access

import org.dom4j.util.IndexedElement;
import org.dom4j.util.IndexedDocumentFactory;

public class IndexedElement extends DefaultElement {
    // Constructor
    public IndexedElement(QName qname);
    public IndexedElement(String name);
    
    // Indexed access methods
    public Element elementByID(String elementID);
    public List<Element> elementsByName(QName qname);
    public List<Element> elementsByName(String name);
    
    // Index management
    public void indexElements();
    public boolean isIndexed();
}

public class IndexedDocumentFactory extends DocumentFactory {
    // Singleton access
    public static IndexedDocumentFactory getInstance();
    
    // Override factory methods to create indexed elements
    public Element createElement(QName qname);
    public Element createElement(String name);
}

Usage Examples:

// Create document with indexed elements
IndexedDocumentFactory factory = IndexedDocumentFactory.getInstance();
SAXReader reader = new SAXReader();
reader.setDocumentFactory(factory);

Document document = reader.read("large-document.xml");
Element root = document.getRootElement();

if (root instanceof IndexedElement) {
    IndexedElement indexedRoot = (IndexedElement) root;
    
    // Fast element lookup by ID
    Element elementById = indexedRoot.elementByID("product-123");
    
    // Fast lookup by name (faster than XPath for large documents)
    List<Element> products = indexedRoot.elementsByName("product");
    List<Element> categories = indexedRoot.elementsByName(QName.get("category", namespace));
}

Performance Optimization Features

Memory-Efficient Processing

// Custom lightweight implementations
public class LightweightDocumentFactory extends DocumentFactory {
    @Override
    public Element createElement(QName qname) {
        return new LightweightElement(qname);
    }
    
    @Override
    public Document createDocument() {
        return new LightweightDocument();
    }
}

// Optimized for memory usage
class LightweightElement extends AbstractElement {
    private QName qname;
    private Object content; // Single object or List for multiple children
    private Object attributes; // Single attribute or Map for multiple
    
    public LightweightElement(QName qname) {
        this.qname = qname;
    }
    
    // Optimized implementations that use less memory
    // ... implementation details
}

Caching and Performance

// XPath compilation caching
public class CachedXPathProcessor {
    private final Map<String, XPath> xpathCache = new ConcurrentHashMap<>();
    private final Map<String, String> namespaces;
    
    public CachedXPathProcessor(Map<String, String> namespaces) {
        this.namespaces = namespaces != null ? Map.copyOf(namespaces) : Map.of();
    }
    
    public XPath getOrCreateXPath(String expression) {
        return xpathCache.computeIfAbsent(expression, expr -> {
            try {
                XPath xpath = DocumentHelper.createXPath(expr);
                if (!namespaces.isEmpty()) {
                    xpath.setNamespaceURIs(namespaces);
                }
                return xpath;
            } catch (InvalidXPathException e) {
                throw new RuntimeException("Invalid XPath: " + expr, e);
            }
        });
    }
    
    // Batch processing methods
    public Map<String, List<Node>> executeQueries(Document document, List<String> expressions) {
        return expressions.parallelStream()
            .collect(Collectors.toConcurrentMap(
                expr -> expr,
                expr -> getOrCreateXPath(expr).selectNodes(document)
            ));
    }
}

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.

Install with Tessl CLI

npx tessl i tessl/maven-org-dom4j--dom4j

docs

advanced-features.md

core-api.md

document-creation.md

index.md

io-operations.md

xpath.md

tile.json