Editor API

The Editor class is the main entry point for DomTrip. It provides high-level operations for loading, editing, and serializing XML documents while preserving formatting.

Constructor

Editor()

Creates a new editor with default configuration.

// Default constructor
Editor editor = new Editor();

// With custom configuration
DomTripConfig config = DomTripConfig.prettyPrint();
Editor configuredEditor = new Editor(config);

// With existing document
String xml = "<project></project>";
Document doc = Document.of(xml);
Editor documentEditor = new Editor(doc);

// With document and configuration
Editor fullEditor = new Editor(doc, DomTripConfig.strict());

Editor(DomTripConfig config)

Creates a new editor with custom configuration.

// Default constructor
Editor editor = new Editor();

// With custom configuration
DomTripConfig config = DomTripConfig.prettyPrint();
Editor configuredEditor = new Editor(config);

// With existing document
String xml = "<project></project>";
Document doc = Document.of(xml);
Editor documentEditor = new Editor(doc);

// With document and configuration
Editor fullEditor = new Editor(doc, DomTripConfig.strict());

Editor(Document document)

Creates a new editor with an existing Document object.

// From string
String xmlString = createConfigXml();
Document doc = Document.of(xmlString);
Editor editor = new Editor(doc);

Editor(Document document, DomTripConfig config)

Creates a new editor with an existing Document and custom configuration.

// Default constructor
Editor editor = new Editor();

// With custom configuration
DomTripConfig config = DomTripConfig.prettyPrint();
Editor configuredEditor = new Editor(config);

// With existing document
String xml = "<project></project>";
Document doc = Document.of(xml);
Editor documentEditor = new Editor(doc);

// With document and configuration
Editor fullEditor = new Editor(doc, DomTripConfig.strict());

Throws: IllegalArgumentException if document is null.

Advanced Constructor Examples

// Working with an existing document
String xmlString = "<project><version>1.0</version></project>";
Document existingDoc = Document.of(xmlString);
Editor editor = new Editor(existingDoc);

// Working with a programmatically created document
Document doc = Document.withRootElement("project");
Editor programmaticEditor = new Editor(doc);

// Continue editing
Element root = programmaticEditor.root();
programmaticEditor.addElement(root, "version", "1.0");

Document Management

document()

Gets the current XML document.

// Get document
Editor editor = new Editor();
editor.createDocument("project");
Document document = editor.document();

// Get root element
Element root = editor.root();

// Create document with root element
editor.createDocument("project");
Element newRoot = editor.root(); // <project></project>

// Serialize to XML
String xml = editor.toXml();

// Pretty printing
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());

root()

Gets the root element of the document.

// Get document
Editor editor = new Editor();
editor.createDocument("project");
Document document = editor.document();

// Get root element
Element root = editor.root();

// Create document with root element
editor.createDocument("project");
Element newRoot = editor.root(); // <project></project>

// Serialize to XML
String xml = editor.toXml();

// Pretty printing
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());

createDocument(String rootElementName)

Creates a new document with the specified root element.

// Get document
Editor editor = new Editor();
editor.createDocument("project");
Document document = editor.document();

// Get root element
Element root = editor.root();

// Create document with root element
editor.createDocument("project");
Element newRoot = editor.root(); // <project></project>

// Serialize to XML
String xml = editor.toXml();

// Pretty printing
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());

Serialization

toXml()

Serializes the document to XML string with preserved formatting.

// Get document
Editor editor = new Editor();
editor.createDocument("project");
Document document = editor.document();

// Get root element
Element root = editor.root();

// Create document with root element
editor.createDocument("project");
Element newRoot = editor.root(); // <project></project>

// Serialize to XML
String xml = editor.toXml();

// Pretty printing
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());

toXml(DomTripConfig config)

Serializes with custom configuration.

String xml = "<root><child>value</child></root>";
Editor editor = new Editor(Document.of(xml));

// Default serialization
String defaultXml = editor.toXml();

// Pretty printed
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());

// Minimal output
String minimalXml = editor.toXml(DomTripConfig.minimal());

Pretty Printing

For pretty printing, use the configuration approach:

// Get document
Editor editor = new Editor();
editor.createDocument("project");
Document document = editor.document();

// Get root element
Element root = editor.root();

// Create document with root element
editor.createDocument("project");
Element newRoot = editor.root(); // <project></project>

// Serialize to XML
String xml = editor.toXml();

// Pretty printing
String prettyXml = editor.toXml(DomTripConfig.prettyPrint());

Element Operations

findElement(String name)

Finds the first element with the specified name in the document.

String xml = "<project><version>1.0</version><dependency><version>2.0</version></dependency></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

// Find first element with name
Element version = root.child("version").orElse(null);

// Find all elements with name using descendants
List<Element> allVersions = root.descendants("version").toList();

// Add new child element
Element child = editor.addElement(root, "newChild");

// Remove element (if it exists)
Element toRemove = root.child("deprecated").orElse(null);
if (toRemove != null) {
    editor.removeElement(toRemove);
}

// Get text content
String content = version != null ? version.textContent() : "";

Returns: The element, or null if not found.

findElements(String name)

Finds all elements with the specified name.

String xml = "<project><version>1.0</version><dependency><version>2.0</version></dependency></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

// Find first element with name
Element version = root.child("version").orElse(null);

// Find all elements with name using descendants
List<Element> allVersions = root.descendants("version").toList();

// Add new child element
Element child = editor.addElement(root, "newChild");

// Remove element (if it exists)
Element toRemove = root.child("deprecated").orElse(null);
if (toRemove != null) {
    editor.removeElement(toRemove);
}

// Get text content
String content = version != null ? version.textContent() : "";

addElement(Element parent, String name)

Adds a new child element to the parent.

String xml = "<project><version>1.0</version><dependency><version>2.0</version></dependency></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

// Find first element with name
Element version = root.child("version").orElse(null);

// Find all elements with name using descendants
List<Element> allVersions = root.descendants("version").toList();

// Add new child element
Element child = editor.addElement(root, "newChild");

// Remove element (if it exists)
Element toRemove = root.child("deprecated").orElse(null);
if (toRemove != null) {
    editor.removeElement(toRemove);
}

// Get text content
String content = version != null ? version.textContent() : "";

Returns: The newly created element.

addElement(Element parent, String name, String textContent)

Adds a new child element with text content.

String xml = "<project></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();

// Add element without content
Element child = editor.addElement(parent, "newChild");

// Add element with text content
Element version = editor.addElement(parent, "version", "1.0.0");

addElements(Element parent, Map<String, String> nameValuePairs)

Batch operation to add multiple child elements.

String xml = "<project></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();

Map<String, String> properties = Map.of(
        "groupId", "com.example",
        "artifactId", "my-app",
        "version", "1.0.0");
editor.addElements(parent, properties);

removeElement(Element element)

Removes an element from its parent.

String xml = "<project><version>1.0</version><dependency><version>2.0</version></dependency></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

// Find first element with name
Element version = root.child("version").orElse(null);

// Find all elements with name using descendants
List<Element> allVersions = root.descendants("version").toList();

// Add new child element
Element child = editor.addElement(root, "newChild");

// Remove element (if it exists)
Element toRemove = root.child("deprecated").orElse(null);
if (toRemove != null) {
    editor.removeElement(toRemove);
}

// Get text content
String content = version != null ? version.textContent() : "";

Text Content Operations

setTextContent(Element element, String content)

Sets the text content of an element.

String xml = "<project><version>1.0</version></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

Element version = root.child("version").orElse(null);

// Get text content
String content = version.textContent();

// Set text content
editor.setTextContent(version, "2.0.0");

getTextContent(Element element)

Gets the text content of an element.

String xml = "<project><version>1.0</version><dependency><version>2.0</version></dependency></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

// Find first element with name
Element version = root.child("version").orElse(null);

// Find all elements with name using descendants
List<Element> allVersions = root.descendants("version").toList();

// Add new child element
Element child = editor.addElement(root, "newChild");

// Remove element (if it exists)
Element toRemove = root.child("deprecated").orElse(null);
if (toRemove != null) {
    editor.removeElement(toRemove);
}

// Get text content
String content = version != null ? version.textContent() : "";

Attribute Operations

setAttribute(Element element, String name, String value)

Sets an attribute value with intelligent formatting preservation and inference.

Formatting Behavior:

  • Existing attributes: Preserves original quote style and whitespace
  • New attributes: Infers formatting from existing attributes on the element
String xml = "<dependency scope='test'></dependency>";
Editor editor = new Editor(Document.of(xml));
Element element = editor.root();

// For XML: <element attr1='existing' attr2="another"/>
editor.setAttribute(element, "attr1", "updated"); // Preserves single quotes
editor.setAttribute(element, "attr3", "new"); // Infers quote style from existing

// Remove attribute
editor.removeAttribute(element, "deprecated");

// Set multiple attributes
Map<String, String> attrs = Map.of(
        "scope", "test",
        "optional", "true");
editor.setAttributes(element, attrs);

// Get attribute value
String scopeValue = element.attribute("scope");

// Check if attribute exists
boolean hasOptional = element.hasAttribute("optional");

getAttribute(Element element, String name)

Gets an attribute value.

String xml = "<dependency scope='test'></dependency>";
Editor editor = new Editor(Document.of(xml));
Element element = editor.root();

// Get attribute value
String scope = element.attribute("scope");

// Set attribute value
editor.setAttribute(element, "optional", "true");

// Remove attribute
editor.removeAttribute(element, "scope");

removeAttribute(Element element, String name)

Removes an attribute.

String xml = "<dependency scope='test'></dependency>";
Editor editor = new Editor(Document.of(xml));
Element element = editor.root();

// For XML: <element attr1='existing' attr2="another"/>
editor.setAttribute(element, "attr1", "updated"); // Preserves single quotes
editor.setAttribute(element, "attr3", "new"); // Infers quote style from existing

// Remove attribute
editor.removeAttribute(element, "deprecated");

// Set multiple attributes
Map<String, String> attrs = Map.of(
        "scope", "test",
        "optional", "true");
editor.setAttributes(element, attrs);

// Get attribute value
String scopeValue = element.attribute("scope");

// Check if attribute exists
boolean hasOptional = element.hasAttribute("optional");

setAttributes(Element element, Map<String, String> attributes)

Sets multiple attributes at once with intelligent formatting.

String xml = "<dependency scope='test'></dependency>";
Editor editor = new Editor(Document.of(xml));
Element element = editor.root();

// For XML: <element attr1='existing' attr2="another"/>
editor.setAttribute(element, "attr1", "updated"); // Preserves single quotes
editor.setAttribute(element, "attr3", "new"); // Infers quote style from existing

// Remove attribute
editor.removeAttribute(element, "deprecated");

// Set multiple attributes
Map<String, String> attrs = Map.of(
        "scope", "test",
        "optional", "true");
editor.setAttributes(element, attrs);

// Get attribute value
String scopeValue = element.attribute("scope");

// Check if attribute exists
boolean hasOptional = element.hasAttribute("optional");

Advanced Attribute Formatting:

For fine-grained control over attribute formatting, you can work with Attribute objects directly:

String xml = "<dependency scope='test'></dependency>";
Editor editor = new Editor(Document.of(xml));
Element element = editor.root();

// For XML: <element attr1='existing' attr2="another"/>
editor.setAttribute(element, "attr1", "updated"); // Preserves single quotes
editor.setAttribute(element, "attr3", "new"); // Infers quote style from existing

// Remove attribute
editor.removeAttribute(element, "deprecated");

// Set multiple attributes
Map<String, String> attrs = Map.of(
        "scope", "test",
        "optional", "true");
editor.setAttributes(element, attrs);

// Get attribute value
String scopeValue = element.attribute("scope");

// Check if attribute exists
boolean hasOptional = element.hasAttribute("optional");

Comment Operations

addComment(Element parent, String content)

Adds a comment as a child of the parent element.

String xml = "<project><version>1.0</version></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();
Element version = parent.child("version").orElse(null);

// Add comment as child
editor.addComment(parent, " This is a comment ");

// Add comment using fluent API
editor.add().comment().to(parent).withContent(" Configuration section ").build();

addCommentBefore(Element element, String content)

Adds a comment before the specified element.

String xml = "<project><version>1.0</version></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();

// Add comment as child of parent
editor.addComment(parent, " Configuration section ");

// Using fluent builder API for comments
editor.add().comment().to(parent).withContent(" End of configuration ").build();

addCommentAfter(Element element, String content)

Adds a comment after the specified element.

String xml = "<project><version>1.0</version></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();

// Add comment as child of parent
editor.addComment(parent, " Configuration section ");

// Using fluent builder API for comments
editor.add().comment().to(parent).withContent(" End of configuration ").build();

Fluent Builder API

add()

Creates a fluent builder for adding nodes.

String xml = "<project></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();

editor.add()
        .element("dependency")
        .to(parent)
        .withAttribute("scope", "test")
        .withText("content")
        .build();

editor.add().comment().to(parent).withContent(" This is a comment ").build();

Configuration

config()

Gets the configuration used by this editor.

DomTripConfig config = DomTripConfig.prettyPrint();
Editor editor = new Editor(config);

// Get the configuration used by this editor
DomTripConfig usedConfig = editor.config();

Exception Handling

The Editor class throws specific exceptions for different error conditions:

  • ParseException: Thrown when XML parsing fails
  • InvalidXmlException: Thrown for invalid editing operations
  • DomTripException: Base exception for other DomTrip errors
try {
    String malformedXml = "<root><unclosed>";
    Editor editor = new Editor(Document.of(malformedXml));
    // ... editing operations
} catch (DomTripException e) {
    // Handle parsing/editing errors
    System.err.println("XML error: " + e.getMessage());
}

Best Practices

1. Check for Null Returns

String xml = "<project><version>1.0</version></project>";
Editor editor = new Editor(Document.of(xml));
Element root = editor.root();

// ✅ Safe navigation using Optional
Optional<Element> element = root.child("optional");
element.ifPresent(el -> editor.setTextContent(el, "value"));

// ✅ Or use null check pattern
Element version = root.child("version").orElse(null);
if (version != null) {
    editor.setTextContent(version, "updated");
}

2. Use Batch Operations

// ✅ Proper resource management
String xml = "<root><child>value</child></root>";
byte[] xmlBytes = xml.getBytes(StandardCharsets.UTF_8);

try (InputStream inputStream = new ByteArrayInputStream(xmlBytes);
        OutputStream outputStream = new ByteArrayOutputStream()) {

    Document doc = Document.of(inputStream);
    doc.toXml(outputStream);
}

// ✅ Type-safe Charset objects
try (InputStream inputStream = new ByteArrayInputStream(xmlBytes);
        OutputStream outputStream = new ByteArrayOutputStream()) {

    Document doc = Document.of(inputStream, StandardCharsets.UTF_8);
    doc.toXml(outputStream, StandardCharsets.UTF_16);
}

// ✅ Automatic detection
try (InputStream inputStream = new ByteArrayInputStream(xmlBytes)) {
    Document doc = Document.of(inputStream);
    Assertions.assertNotNull(doc);
}

3. Handle Exceptions Appropriately

// ✅ Specific exception handling
try {
    String xmlContent = "<root><unclosed>";
    Document doc = Document.of(xmlContent);
    Editor editor = new Editor(doc);
} catch (DomTripException e) {
    // Handle DomTrip errors (including parsing errors)
    System.err.println("DomTrip error: " + e.getMessage());
}

Thread Safety

The Editor class is not thread-safe. If you need to use an editor instance across multiple threads, you must provide external synchronization.

String xml = "<project></project>";
Editor editor = new Editor(Document.of(xml));
Element parent = editor.root();

// ✅ Thread-safe usage (conceptual example)
synchronized (editor) {
    editor.addElement(parent, "child", "value");
    String result = editor.toXml();
    Assertions.assertTrue(result.contains("child"));
}

// ✅ Or use separate editor instances per thread
Editor editorForThread = new Editor(Document.of(xml));
editorForThread.addElement(editorForThread.root(), "thread-child", "value");