Element Positioning
DomTrip's Editor provides precise control over element positioning, allowing you to insert elements at specific locations within the XML document structure.
Overview
The positioning features allow you to:
- Insert at specific index - Place elements at exact positions within a parent
- Insert before elements - Add elements immediately before existing elements
- Insert after elements - Add elements immediately after existing elements
- Maintain formatting - Preserve indentation and whitespace patterns
Basic Usage
Insert at Specific Position
Use insertElementAt()
to insert an element at a specific index:
Document doc = Document.of(
"""
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</dependency>
</dependencies>
""");
Editor editor = new Editor(doc);
Element dependencies = doc.root();
// Insert a new dependency at index 1 (between existing dependencies)
Element newDep = editor.insertElementAt(dependencies, 1, "dependency");
editor.addElement(newDep, "groupId", "mockito");
editor.addElement(newDep, "artifactId", "mockito-core");
String result = editor.toXml();
Insert Before Element
Use insertElementBefore()
to insert an element before an existing element:
Document doc = Document.of(
"""
<project>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<build>
<plugins/>
</build>
</project>
""");
Editor editor = new Editor(doc);
Element build = doc.root().child("build").orElseThrow();
// Insert properties section before build
Element properties = editor.insertElementBefore(build, "properties");
editor.addElement(properties, "maven.compiler.source", "17");
editor.addElement(properties, "maven.compiler.target", "17");
String result = editor.toXml();
Insert After Element
Use insertElementAfter()
to insert an element after an existing element:
Document doc = Document.of(
"""
<project>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
</project>
""");
Editor editor = new Editor(doc);
Element version = doc.root().child("version").orElseThrow();
// Insert packaging after version
Element packaging = editor.insertElementAfter(version, "packaging");
editor.setTextContent(packaging, "jar");
String result = editor.toXml();
Advanced Usage
Insert with Text Content
All positioning methods support optional text content:
// Insert at position with text content
Element element = editor.insertElementAt(parent, 2, "description", "Project description");
// Insert before with text content
Element name = editor.insertElementBefore(existing, "name", "My Project");
// Insert after with text content
Element url = editor.insertElementAfter(existing, "url", "https://example.com");
Complex Positioning Scenarios
Handle complex document structures with multiple insertions:
Document doc = Document.of("""
<project>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
""");
Editor editor = new Editor(doc);
Element modelVersion = doc.root().child("modelVersion").orElseThrow();
Element dependencies = doc.root().child("dependencies").orElseThrow();
Element junit = dependencies.child("dependency").orElseThrow();
// Insert project coordinates after modelVersion
editor.insertElementAfter(modelVersion, "groupId", "com.example");
editor.insertElementAfter(doc.root().child("groupId").orElseThrow(), "artifactId", "my-app");
editor.insertElementAfter(doc.root().child("artifactId").orElseThrow(), "version", "1.0.0");
// Insert additional dependency before junit
Element mockito = editor.insertElementBefore(junit, "dependency");
editor.addElement(mockito, "groupId", "org.mockito");
editor.addElement(mockito, "artifactId", "mockito-core");
// Insert build section after dependencies
Element build = editor.insertElementAfter(dependencies, "build");
Element plugins = editor.addElement(build, "plugins");
Working with Indices
Understand how indices work with XML nodes:
Element parent = doc.root();
// Index 0: insert at the beginning
Element first = editor.insertElementAt(parent, 0, "first");
// Index parent.nodeCount(): append at the end
Element last = editor.insertElementAt(parent, parent.nodeCount(), "last");
// Indices account for all nodes (elements, text, comments)
// Use carefully when whitespace nodes are present
Method Reference
insertElementAt(Element parent, int index, String elementName)
Inserts a new element at the specified position within the parent.
Parameters:
parent
- The parent element to insert intoindex
- The position to insert at (0-based)elementName
- The name of the new element
Returns:
Element
- The newly created element
Throws:
DomTripException
- If the insertion fails or index is out of bounds
insertElementAt(Element parent, int index, String elementName, String textContent)
Inserts a new element with text content at the specified position.
Parameters:
parent
- The parent element to insert intoindex
- The position to insert at (0-based)elementName
- The name of the new elementtextContent
- The text content for the element
Returns:
Element
- The newly created element
insertElementBefore(Element referenceElement, String elementName)
Inserts a new element before the specified reference element.
Parameters:
referenceElement
- The element to insert beforeelementName
- The name of the new element
Returns:
Element
- The newly created element
Throws:
DomTripException
- If the reference element has no parent
insertElementBefore(Element referenceElement, String elementName, String textContent)
Inserts a new element with text content before the reference element.
insertElementAfter(Element referenceElement, String elementName)
Inserts a new element after the specified reference element.
Parameters:
referenceElement
- The element to insert afterelementName
- The name of the new element
Returns:
Element
- The newly created element
insertElementAfter(Element referenceElement, String elementName, String textContent)
Inserts a new element with text content after the reference element.
Error Handling
The positioning methods include comprehensive validation:
// Invalid index bounds
assertThrows(DomTripException.class, () -> {
editor.insertElementAt(parent, -1, "invalid"); // Negative index
});
assertThrows(DomTripException.class, () -> {
editor.insertElementAt(parent, parent.nodeCount() + 1, "invalid"); // Too large
});
// Null or empty element names
assertThrows(DomTripException.class, () -> {
editor.insertElementAt(parent, 0, null);
});
assertThrows(DomTripException.class, () -> {
editor.insertElementBefore(reference, "");
});
// Reference element without parent
Element orphan = new Element("orphan");
assertThrows(DomTripException.class, () -> {
editor.insertElementBefore(orphan, "newElement");
});
Whitespace and Formatting
Positioning methods automatically handle whitespace and indentation:
// The editor infers appropriate indentation from the parent
Element newElement = editor.insertElementAt(parent, 1, "child");
// Whitespace is automatically applied based on surrounding elements
String indentation = editor.whitespaceManager().inferIndentation(parent);
// newElement will have appropriate preceding whitespace
Best Practices
- Use semantic positioning - Choose the method that best expresses your intent
- Consider document structure - Understand the logical flow of your XML
- Handle edge cases - Always validate indices and reference elements
- Combine with other features - Use positioning with element creation and modification
- Test thoroughly - Verify element order in complex scenarios
Integration Examples
Building a Maven POM
Editor editor = new Editor();
editor.createDocument("project");
Element project = editor.root();
// Add basic coordinates
editor.addElement(project, "modelVersion", "4.0.0");
editor.addElement(project, "groupId", "com.example");
editor.addElement(project, "artifactId", "my-app");
editor.addElement(project, "version", "1.0.0");
// Insert packaging after version
Element version = project.child("version").orElseThrow();
editor.insertElementAfter(version, "packaging", "jar");
// Add dependencies section
Element dependencies = editor.addElement(project, "dependencies");
Element junit = editor.addElement(dependencies, "dependency");
editor.addElement(junit, "groupId", "junit");
editor.addElement(junit, "artifactId", "junit");
editor.addElement(junit, "scope", "test");
// Insert mockito before junit
Element mockito = editor.insertElementBefore(junit, "dependency");
editor.addElement(mockito, "groupId", "org.mockito");
editor.addElement(mockito, "artifactId", "mockito-core");
editor.addElement(mockito, "scope", "test");
Reordering Elements
// Move an element to a different position
Element elementToMove = parent.child("target").orElseThrow();
Element referenceElement = parent.child("reference").orElseThrow();
// Remove from current position
parent.removeNode(elementToMove);
// Insert at new position
editor.insertElementBefore(referenceElement, elementToMove.name(), elementToMove.textContent());