Builder Patterns
DomTrip provides sophisticated builder patterns that enable fluent, type-safe XML document construction and modification. These patterns make it easy to create complex XML structures while maintaining readability and compile-time safety.
Core Builder Concepts
Fluent Interface Design
DomTrip's builders follow the fluent interface pattern, allowing method chaining for natural, readable code:
Editor editor = new Editor();
editor.createDocument("project");
Element root = editor.root();
// Add namespace attribute
editor.setAttribute(root, "xmlns", "http://maven.apache.org/POM/4.0.0");
// Add child elements using fluent API
editor.addElement(root, "groupId", "com.example");
editor.addElement(root, "artifactId", "my-project");
editor.addElement(root, "version", "1.0.0");
Document doc = editor.document();
Type-Safe Construction
Builders provide compile-time safety and IDE support with method completion and type checking.
Document Builder
Creating New Documents
The Editor class serves as the main entry point for document creation:
// Create empty document
Document doc = Document.of();
// Parse from XML string
String xmlString = "<root><child>value</child></root>";
Document doc2 = Document.of(xmlString);
// Create with root element
Document doc3 = Document.withRootElement("project");
// Create with XML declaration
Document doc4 = Document.withXmlDeclaration("1.0", "UTF-8");
Document Configuration
Configure document-level settings during creation:
DomTripConfig config = DomTripConfig.prettyPrint();
Editor editor = new Editor(config);
editor.createDocument("root");
Document doc = editor.document();
Element Builder
Basic Element Creation
Create elements with various content types:
Editor editor = new Editor();
editor.createDocument("root");
Element root = editor.root();
// Simple element with text
editor.addElement(root, "name", "John Doe");
// Element with attributes
Element person = editor.addElement(root, "person");
editor.setAttribute(person, "id", "123");
editor.setAttribute(person, "active", "true");
editor.setTextContent(person, "John Doe");
// Nested elements
Element personNested = editor.addElement(root, "person");
editor.addElement(personNested, "firstName", "John");
editor.addElement(personNested, "lastName", "Doe");
editor.addElement(personNested, "email", "john@example.com");
Advanced Element Operations
Editor editor = new Editor();
editor.createDocument("root");
Element root = editor.root();
Person person = new Person("123", "john@example.com", true);
// Conditional element addition
Element personElement = editor.addElement(root, "person");
editor.setAttribute(personElement, "id", person.getId());
if (person.getEmail() != null) {
editor.addElement(personElement, "email", person.getEmail());
}
if (person.isActive()) {
editor.addElement(personElement, "status", "active");
}
// Element positioning using batch operations
Element dependencies = editor.addElement(root, "dependencies");
Map<String, String> dependencyInfo = Map.of(
"groupId", "junit",
"artifactId", "junit");
Element dependency = editor.addElement(dependencies, "dependency");
editor.addElements(dependency, dependencyInfo);
editor.addElement(dependency, "scope", "test");
Attribute Builder
Attribute Management
DomTrip provides flexible attribute handling with quote style preservation:
Editor editor = new Editor();
editor.createDocument("root");
Element element = editor.addElement(editor.root(), "test");
boolean condition = true;
// Basic attributes
editor.setAttribute(element, "name", "value");
// Attribute with specific quote style
Attribute attr = Attribute.of("id", "123", QuoteStyle.SINGLE);
element.attributeObject("id", attr);
// Conditional attributes
if (condition) {
editor.setAttribute(element, "optional", "value");
}
// Namespace-aware attributes (using string form)
editor.setAttribute(element, "xml:lang", "en");
Attribute Modification
Editor editor = new Editor();
editor.createDocument("root");
Element element = editor.addElement(editor.root(), "test");
// Set initial attributes
editor.setAttribute(element, "id", "old-value");
editor.setAttribute(element, "class", "old-class");
editor.setAttribute(element, "deprecated", "true");
editor.setAttribute(element, "version", "1.0");
// Update existing attributes
editor.setAttribute(element, "id", "new-value");
// Change quote style (create new attribute with different quotes)
Attribute classAttr = Attribute.of("class", "old-class", QuoteStyle.SINGLE);
element.attributeObject("class", classAttr);
// Remove attributes
editor.removeAttribute(element, "deprecated");
// Attribute transformation (manual approach)
String version = element.attribute("version");
if (version != null) {
editor.setAttribute(element, "version", version + "-SNAPSHOT");
}
Namespace Builder
Namespace Declaration
Handle XML namespaces with builder support:
Editor editor = new Editor();
editor.createDocument("project");
Element root = editor.root();
// Declare namespaces using namespace declaration methods
root.namespaceDeclaration("", "http://maven.apache.org/POM/4.0.0");
root.namespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
editor.setAttribute(root, "xsi:schemaLocation", "...");
// Namespace-aware element creation
Element nsElement = editor.addElement(root, "ns:element");
editor.addElement(nsElement, "ns:child", "content");
Comment and Processing Instruction Builders
Adding Comments
Editor editor = new Editor();
editor.createDocument("root");
Element root = editor.root();
// Simple comments
editor.addComment(root, " This is a comment ");
// Positioned comments using fluent API
Element section = editor.addElement(root, "section");
editor.add()
.comment()
.to(section)
.withContent(" Start of configuration ")
.build();
editor.addElement(section, "config", "value");
editor.add().comment().to(section).withContent(" End of configuration ").build();
// Multi-line comments
editor.addComment(
root,
"""
This is a multi-line comment
that spans several lines
and provides detailed information
""");
Processing Instructions
Editor editor = new Editor();
editor.createDocument("root");
// Processing instructions using direct creation
ProcessingInstruction stylesheet =
ProcessingInstruction.of("xml-stylesheet", "type=\"text/xsl\" href=\"transform.xsl\"");
editor.document().addNode(stylesheet);
Advanced Builder Patterns
Custom Builder Extensions
Create domain-specific builders for common patterns:
MavenPomBuilder pomBuilder = new MavenPomBuilder();
Document pom = pomBuilder
.groupId("com.example")
.artifactId("my-project")
.version("1.0.0")
.build();
Builder Composition
Combine multiple builders for complex document structures:
Document pom = new MavenPomBuilder()
.groupId("com.example")
.artifactId("my-app")
.version("1.0.0")
.addDependency("junit", "junit", "4.13.2", "test")
.addDependency("org.slf4j", "slf4j-api", "1.7.32", null)
.build();
Error Handling in Builders
Validation and Error Recovery
String value = "test-value";
String content = "test-content";
try {
Editor editor = new Editor();
editor.createDocument("root");
Element root = editor.root();
Element child = editor.addElement(root, "child");
editor.setAttribute(child, "required", value);
editor.setTextContent(child, content);
Document doc = editor.document();
Assertions.assertNotNull(doc);
} catch (DomTripException e) {
// Handle validation errors
System.err.println("Failed to build document: " + e.getMessage());
// Provide fallback or recovery logic
}
Builder State Validation
Editor editor = new Editor();
editor.createDocument("root");
Element root = editor.root();
// Validate state before building
if (root.children().count() == 0) {
editor.addElement(root, "default-child", "default-value");
}
// Ensure required elements exist
if (root.child("version").isEmpty()) {
editor.addElement(root, "version", "1.0.0");
}
Document doc = editor.document();
Best Practices
1. Use Method Chaining Judiciously
// ✅ Good - short chains on one line are readable
Element dependency = Element.of("dependency").attribute("scope", "test").attribute("optional", "false");
// ✅ Good - break longer chains across multiple lines
Element person = Element.of("person")
.attribute("id", "123")
.attribute("active", "true")
.attribute("role", "developer");
// ✅ Good - combine chaining with method calls
Element project = Element.of("project").attribute("xmlns", "http://maven.apache.org/POM/4.0.0");
project.addNode(Element.of("groupId").textContent("com.example"));
project.addNode(Element.of("artifactId").textContent("my-app"));
project.addNode(Element.of("version").textContent("1.0.0"));
2. Leverage Type Safety
Editor editor = new Editor();
editor.createDocument("root");
Element root = editor.root();
// ✅ Good - use type-safe methods
Element element = editor.addElement(root, "element");
editor.setAttribute(element, "id", "123");
// ✅ Good - leverage Optional for safe navigation
String value = root.child("element")
.flatMap(e -> e.child("value"))
.map(Element::textContent)
.orElse("default");
// ✅ Good - use streams for type-safe filtering
long count = root.children("element").filter(e -> e.hasAttribute("id")).count();
3. Handle Namespaces Consistently
Editor editor = new Editor();
editor.createDocument("project");
Element root = editor.root();
// ✅ Good - declare namespaces at the root
root.namespaceDeclaration("", "http://maven.apache.org/POM/4.0.0");
root.namespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
// ✅ Good - use consistent namespace prefixes
editor.setAttribute(root, "xsi:schemaLocation", "http://maven.apache.org/POM/4.0.0 ...");
// ✅ Good - namespace-aware element creation
Element dependency = editor.addElement(root, "dependency");
editor.addElement(dependency, "groupId", "com.example");
// ❌ Avoid - mixing prefixed and unprefixed elements inconsistently
// ❌ Avoid - declaring the same namespace multiple times with different prefixes
4. Use Builders for Complex Structures
For repetitive or complex XML patterns, create custom builders that encapsulate the logic and provide a clean API.
Integration with DomTrip Features
Builder patterns work seamlessly with all DomTrip features:
- Formatting Preservation: Builders respect existing formatting when modifying documents
- Namespace Support: Full namespace-aware building capabilities
- Configuration: Builders honor DomTripConfig settings
- Error Handling: Comprehensive error reporting and recovery
The builder patterns in DomTrip provide a powerful, flexible foundation for XML document construction while maintaining the library's core principles of formatting preservation and ease of use.