Dependency Alignment

DomTrip can detect how your project manages dependency versions and add or transform dependencies to follow those conventions consistently. This is powered by the AlignOptions configuration and the convention detection API.

Convention Detection

DomTrip analyzes your POM to detect three aspects of your dependency versioning strategy:

String pom = """
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0">
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.example</groupId>
          <artifactId>my-project</artifactId>
          <version>1.0.0</version>
          <properties>
            <slf4j.version>2.0.7</slf4j.version>
            <guava.version>32.1.2-jre</guava.version>
          </properties>
          <dependencyManagement>
            <dependencies>
              <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
              </dependency>
              <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
              </dependency>
            </dependencies>
          </dependencyManagement>
          <dependencies>
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
            </dependency>
            <dependency>
              <groupId>com.google.guava</groupId>
              <artifactId>guava</artifactId>
            </dependency>
          </dependencies>
        </project>
        """;

PomEditor editor = new PomEditor(Document.of(pom));

// Detect individual conventions
AlignOptions.VersionStyle style = editor.dependencies().detectVersionStyle();
// MANAGED (most dependencies are version-less)

AlignOptions.VersionSource source = editor.dependencies().detectVersionSource();
// PROPERTY (versions use ${...} references)

AlignOptions.PropertyNamingConvention naming = editor.dependencies().detectPropertyNamingConvention();
// DOT_SUFFIX (e.g., "slf4j.version")

// Detect all conventions at once
AlignOptions detected = editor.dependencies().detectConventions();

What Gets Detected

Convention Values Description
VersionStyle INLINE, MANAGED Whether versions are on dependencies directly or in <dependencyManagement>
VersionSource LITERAL, PROPERTY Whether versions are literal values (5.9.2) or property references (${junit.version})
PropertyNamingConvention DOT_SUFFIX, DASH_SUFFIX, CAMEL_CASE, DOT_PREFIX How version properties are named

Property Naming Examples

Convention Example
DOT_SUFFIX junit-jupiter.version
DASH_SUFFIX junit-jupiter-version
CAMEL_CASE junitJupiterVersion
DOT_PREFIX version.junit-jupiter

Adding Aligned Dependencies

Use addAligned() to add dependencies that automatically follow your project's conventions:

String pom = """
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0">
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.example</groupId>
          <artifactId>my-project</artifactId>
          <version>1.0.0</version>
          <properties>
            <slf4j.version>2.0.7</slf4j.version>
          </properties>
          <dependencyManagement>
            <dependencies>
              <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
              </dependency>
            </dependencies>
          </dependencyManagement>
          <dependencies>
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
            </dependency>
          </dependencies>
        </project>
        """;

PomEditor editor = new PomEditor(Document.of(pom));

// Add a dependency aligned with auto-detected conventions
// Detects: MANAGED + PROPERTY + DOT_SUFFIX → creates property and managed entry
Coordinates jackson = Coordinates.of("com.fasterxml.jackson.core", "jackson-core", "2.15.2");
editor.dependencies().addAligned(jackson);

// Add with explicit options
Coordinates junit = Coordinates.of("org.junit.jupiter", "junit-jupiter", "5.10.0");
editor.dependencies()
        .addAligned(junit, AlignOptions.builder().scope("test").build());

// Force a specific style regardless of detected conventions
Coordinates mockito = Coordinates.of("org.mockito", "mockito-core", "5.5.0");
editor.dependencies()
        .addAligned(
                mockito,
                AlignOptions.builder()
                        .versionStyle(AlignOptions.VersionStyle.INLINE)
                        .versionSource(AlignOptions.VersionSource.LITERAL)
                        .scope("test")
                        .build());

Aligning Existing Dependencies

Transform existing dependencies to match a target convention:

String pom = """
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0">
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.example</groupId>
          <artifactId>my-project</artifactId>
          <version>1.0.0</version>
          <dependencies>
            <dependency>
              <groupId>com.google.guava</groupId>
              <artifactId>guava</artifactId>
              <version>32.1.2-jre</version>
            </dependency>
          </dependencies>
        </project>
        """;

PomEditor editor = new PomEditor(Document.of(pom));

// Align an existing dependency to use managed + property style
Coordinates guava = Coordinates.of("com.google.guava", "guava", null);
editor.dependencies()
        .alignDependency(
                guava,
                AlignOptions.builder()
                        .versionStyle(AlignOptions.VersionStyle.MANAGED)
                        .versionSource(AlignOptions.VersionSource.PROPERTY)
                        .namingConvention(AlignOptions.PropertyNamingConvention.DOT_SUFFIX)
                        .build());
// Result: version moved to dependencyManagement with ${guava.version} property

Aligning All Dependencies

Apply conventions consistently across all dependencies in a single call:

String pom = """
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0">
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.example</groupId>
          <artifactId>my-project</artifactId>
          <version>1.0.0</version>
          <dependencies>
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
              <version>2.0.7</version>
            </dependency>
            <dependency>
              <groupId>com.google.guava</groupId>
              <artifactId>guava</artifactId>
              <version>32.1.2-jre</version>
            </dependency>
          </dependencies>
        </project>
        """;

PomEditor editor = new PomEditor(Document.of(pom));

// Align all dependencies to use managed + property style
int changed = editor.dependencies()
        .alignAllDependencies(AlignOptions.builder()
                .versionStyle(AlignOptions.VersionStyle.MANAGED)
                .versionSource(AlignOptions.VersionSource.PROPERTY)
                .build());

Updating Managed Dependencies (Aligned)

Use updateManagedDependencyAligned() to add or update entries in <dependencyManagement> while following the project's detected conventions. Where updateManagedDependency() stores the version string you pass in directly, this method creates version properties when the project convention calls for them:

String pom = """
        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0">
          <modelVersion>4.0.0</modelVersion>
          <groupId>com.example</groupId>
          <artifactId>my-project</artifactId>
          <version>1.0.0</version>
          <properties>
            <slf4j.version>2.0.7</slf4j.version>
          </properties>
          <dependencyManagement>
            <dependencies>
              <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
              </dependency>
            </dependencies>
          </dependencyManagement>
          <dependencies>
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
            </dependency>
          </dependencies>
        </project>
        """;

PomEditor editor = new PomEditor(Document.of(pom));

// Add a new managed dependency following auto-detected conventions
// Detects PROPERTY + DOT_SUFFIX → creates property and uses ${...} reference
Coordinates jackson = Coordinates.of("com.fasterxml.jackson.core", "jackson-core", "2.15.2");
editor.dependencies().updateManagedDependencyAligned(true, jackson);

// Use explicit options to force property-backed version
Coordinates guava = Coordinates.of("com.google.guava", "guava", "33.0.0-jre");
editor.dependencies()
        .updateManagedDependencyAligned(
                true,
                guava,
                AlignOptions.builder()
                        .versionSource(AlignOptions.VersionSource.PROPERTY)
                        .namingConvention(AlignOptions.PropertyNamingConvention.DOT_SUFFIX)
                        .build());

// Use explicit property name to update an existing managed dependency
Coordinates slf4j = Coordinates.of("org.slf4j", "slf4j-api", "2.0.13");
editor.dependencies()
        .updateManagedDependencyAligned(
                false,
                slf4j,
                AlignOptions.builder().propertyName("slf4j.version").build());

AlignOptions

AlignOptions controls how alignment operations behave. Use the builder for explicit control, or AlignOptions.defaults() to auto-detect everything.

Builder API

// Auto-detect all conventions
AlignOptions options = AlignOptions.defaults();

// Force managed + property style
AlignOptions options = AlignOptions.builder()
    .versionStyle(AlignOptions.VersionStyle.MANAGED)
    .versionSource(AlignOptions.VersionSource.PROPERTY)
    .build();

// Force specific property name
AlignOptions options = AlignOptions.builder()
    .versionSource(AlignOptions.VersionSource.PROPERTY)
    .propertyName("junit.version")
    .build();

// Custom property naming pattern
AlignOptions options = AlignOptions.builder()
    .versionSource(AlignOptions.VersionSource.PROPERTY)
    .propertyNameGenerator(coords ->
        coords.groupId() + "." + coords.artifactId() + ".version")
    .build();

// Add as test dependency
AlignOptions options = AlignOptions.builder()
    .scope("test")
    .build();

Option Fields

Field Type Description
versionStyle VersionStyle INLINE or MANAGED; null = auto-detect
versionSource VersionSource LITERAL or PROPERTY; null = auto-detect
namingConvention PropertyNamingConvention Property naming pattern; null = auto-detect
propertyName String Explicit property name override
propertyNameGenerator Function<Coordinates, String> Custom property name generator
scope String Maven dependency scope (e.g., "test")

Precedence

When resolving property names, the precedence is:

  1. Explicit propertyName (highest)
  2. Custom propertyNameGenerator
  3. namingConvention
  4. Auto-detected convention (lowest)

Next Steps