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.