diff --git a/api/maven-api-model/src/main/mdo/maven.mdo b/api/maven-api-model/src/main/mdo/maven.mdo index 74aff7785e54..1a08abffb1d7 100644 --- a/api/maven-api-model/src/main/mdo/maven.mdo +++ b/api/maven-api-model/src/main/mdo/maven.mdo @@ -1204,6 +1204,20 @@ ]]> + + id + 4.2.0+ + + + + String + groupId 3.0.0+ @@ -1440,6 +1454,20 @@ ]]> + + id + 4.2.0+ + + + + String + groupId 4.0.0+ @@ -1863,6 +1891,20 @@ 4.1.0+ Parent + + gav + 4.2.0+ + + + + String + classifier 4.1.0+ diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelNormalizer.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelNormalizer.java index f36918f770aa..b903df0b384f 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelNormalizer.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelNormalizer.java @@ -29,6 +29,8 @@ import org.apache.maven.api.di.Singleton; import org.apache.maven.api.model.Build; import org.apache.maven.api.model.Dependency; +import org.apache.maven.api.model.Exclusion; +import org.apache.maven.api.model.Mixin; import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Plugin; import org.apache.maven.api.services.ModelBuilderRequest; @@ -49,6 +51,9 @@ public class DefaultModelNormalizer implements ModelNormalizer { public Model mergeDuplicates(Model model, ModelBuilderRequest request, ModelProblemCollector problems) { Model.Builder builder = Model.newBuilder(model); + // Expand id attributes on mixins + builder.mixins(injectList(model.getMixins(), this::expandMixinGav)); + Build build = model.getBuild(); if (build != null) { List plugins = build.getPlugins(); @@ -76,15 +81,20 @@ public Model mergeDuplicates(Model model, ModelBuilderRequest request, ModelProb * the way 2.x works. When we're in strict mode, the removal of duplicates just saves other merging steps from * aftereffects and bogus error messages. */ + // Expand id attributes on dependencies (and their exclusions), then deduplicate List dependencies = model.getDependencies(); - Map normalized = new LinkedHashMap<>(dependencies.size() * 2); + List expanded = injectList(dependencies, this::expandDependencyId); + if (expanded != null) { + dependencies = expanded; + } + Map normalizedDeps = new LinkedHashMap<>(dependencies.size() * 2); for (Dependency dependency : dependencies) { - normalized.put(dependency.getManagementKey(), dependency); + normalizedDeps.put(dependency.getManagementKey(), dependency); } - if (dependencies.size() != normalized.size()) { - builder.dependencies(normalized.values()); + if (expanded != null || dependencies.size() != normalizedDeps.size()) { + builder.dependencies(normalizedDeps.values()); } return builder.build(); @@ -144,4 +154,92 @@ private List injectList(List list, UnaryOperator modifer) { } return newList; } + + /** + * Expands the {@code id} attribute on a dependency into its component fields. + * The id format is {@code groupId:artifactId:version}. + */ + Dependency expandDependencyId(Dependency d) { + String id = d.getId(); + if (id == null || id.isEmpty()) { + // No id attribute, but still expand exclusion ids + List expanded = injectList(d.getExclusions(), this::expandExclusionId); + return expanded != null ? d.withExclusions(expanded) : d; + } + String[] parts = id.split(":"); + if (parts.length != 3) { + // Invalid format — will be caught by the validator + return d; + } + Dependency.Builder builder = Dependency.newBuilder(d); + if (isBlank(d.getGroupId())) { + builder.groupId(parts[0]); + } + if (isBlank(d.getArtifactId())) { + builder.artifactId(parts[1]); + } + if (isBlank(d.getVersion())) { + builder.version(parts[2]); + } + List expanded = injectList(d.getExclusions(), this::expandExclusionId); + if (expanded != null) { + builder.exclusions(expanded); + } + return builder.build(); + } + + /** + * Expands the {@code id} attribute on an exclusion into its component fields. + * The id format is {@code groupId:artifactId}. + */ + Exclusion expandExclusionId(Exclusion e) { + String id = e.getId(); + if (id == null || id.isEmpty()) { + return e; + } + String[] parts = id.split(":"); + if (parts.length != 2) { + // Invalid format — will be caught by the validator + return e; + } + Exclusion.Builder builder = Exclusion.newBuilder(e); + if (isBlank(e.getGroupId())) { + builder.groupId(parts[0]); + } + if (isBlank(e.getArtifactId())) { + builder.artifactId(parts[1]); + } + return builder.build(); + } + + /** + * Expands the {@code id} (XML attribute) / {@code gav} (Java field) on a mixin + * into its component fields. The format is {@code groupId:artifactId:version}. + */ + Mixin expandMixinGav(Mixin m) { + String gav = m.getGav(); + if (gav == null || gav.isEmpty()) { + return m; + } + String[] parts = gav.split(":"); + if (parts.length != 3) { + // Invalid format — will be caught by the validator + return m; + } + Mixin.Builder builder = Mixin.newBuilder(m); + if (isBlank(m.getGroupId())) { + builder.groupId(parts[0]); + } + if (isBlank(m.getArtifactId())) { + builder.artifactId(parts[1]); + } + if (isBlank(m.getVersion())) { + builder.version(parts[2]); + } + return builder.build(); + } + + private static boolean isBlank(String s) { + return s == null || s.isEmpty(); + } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java index bec34fe0fa76..03791aaee4c1 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java @@ -237,7 +237,8 @@ private static boolean objectsEqual(Object obj1, Object obj2) { */ private static boolean dependenciesEqual( org.apache.maven.api.model.Dependency dep1, org.apache.maven.api.model.Dependency dep2) { - return Objects.equals(dep1.getGroupId(), dep2.getGroupId()) + return Objects.equals(dep1.getId(), dep2.getId()) + && Objects.equals(dep1.getGroupId(), dep2.getGroupId()) && Objects.equals(dep1.getArtifactId(), dep2.getArtifactId()) && Objects.equals(dep1.getVersion(), dep2.getVersion()) && Objects.equals(dep1.getType(), dep2.getType()) @@ -286,6 +287,7 @@ private static int computeHashCode(Object obj) { */ private static int dependencyHashCode(org.apache.maven.api.model.Dependency dep) { return Objects.hash( + dep.getId(), dep.getGroupId(), dep.getArtifactId(), dep.getVersion(), diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java index 72917cd7c635..324d01add40b 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java @@ -57,6 +57,7 @@ import org.apache.maven.api.model.Exclusion; import org.apache.maven.api.model.InputLocation; import org.apache.maven.api.model.InputLocationTracker; +import org.apache.maven.api.model.Mixin; import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Parent; import org.apache.maven.api.model.Plugin; @@ -372,6 +373,54 @@ && equals(parent.getArtifactId(), model.getArtifactId())) { // Validate each mixin for (Parent mixin : model.getMixins()) { + // Validate id/gav attribute format and conflict with child elements + if (mixin instanceof Mixin m + && m.getGav() != null + && !m.getGav().isEmpty()) { + String gav = m.getGav(); + String[] parts = gav.split(":"); + if (parts.length != 3) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + "mixins.mixin.id", + null, + "must have the format 'groupId:artifactId:version' but is '" + gav + "'.", + mixin); + } + if (mixin.getGroupId() != null && !mixin.getGroupId().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + "mixins.mixin.groupId", + null, + "must not be specified when the 'id' attribute is used.", + mixin); + } + if (mixin.getArtifactId() != null && !mixin.getArtifactId().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + "mixins.mixin.artifactId", + null, + "must not be specified when the 'id' attribute is used.", + mixin); + } + if (mixin.getVersion() != null && !mixin.getVersion().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + "mixins.mixin.version", + null, + "must not be specified when the 'id' attribute is used.", + mixin); + } + } + if (mixin.getRelativePath() != null && !mixin.getRelativePath().isEmpty() && (mixin.getGroupId() != null && !mixin.getGroupId().isEmpty() @@ -1138,7 +1187,22 @@ private void validate20RawDependencies( Map index = new HashMap<>(); for (Dependency dependency : dependencies) { - String key = dependency.getManagementKey(); + // When 'id' attribute is set, use it for the key since groupId/artifactId + // have not yet been expanded (normalization runs after validation) + String key; + if (dependency.getId() != null && !dependency.getId().isEmpty()) { + key = dependency.getId(); + } else { + key = dependency.getManagementKey(); + } + + // Validate id attribute format and conflict with child elements + validateDependencyIdAttribute(problems, dependency, prefix + prefix2); + + // Validate id attribute on exclusions + for (Exclusion exclusion : dependency.getExclusions()) { + validateExclusionIdAttribute(problems, exclusion, prefix + prefix2 + "exclusions.exclusion."); + } if ("import".equals(dependency.getScope())) { if (!"pom".equals(dependency.getType())) { @@ -1255,6 +1319,102 @@ private void validate20RawDependencies( } } + /** + * Validates the {@code id} attribute on a dependency element. + * The id must have the format {@code groupId:artifactId:version} and must not + * be specified alongside individual groupId, artifactId, or version child elements. + */ + private void validateDependencyIdAttribute(ModelProblemCollector problems, Dependency dependency, String prefix) { + String id = dependency.getId(); + if (id == null || id.isEmpty()) { + return; + } + String[] parts = id.split(":"); + if (parts.length != 3) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "id", + null, + "must have the format 'groupId:artifactId:version' but is '" + id + "'.", + dependency); + } + if (dependency.getGroupId() != null && !dependency.getGroupId().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "groupId", + null, + "must not be specified when the 'id' attribute is used.", + dependency); + } + if (dependency.getArtifactId() != null && !dependency.getArtifactId().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "artifactId", + null, + "must not be specified when the 'id' attribute is used.", + dependency); + } + if (dependency.getVersion() != null && !dependency.getVersion().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "version", + null, + "must not be specified when the 'id' attribute is used.", + dependency); + } + } + + /** + * Validates the {@code id} attribute on an exclusion element. + * The id must have the format {@code groupId:artifactId} and must not + * be specified alongside individual groupId or artifactId child elements. + */ + private void validateExclusionIdAttribute(ModelProblemCollector problems, Exclusion exclusion, String prefix) { + String id = exclusion.getId(); + if (id == null || id.isEmpty()) { + return; + } + String[] parts = id.split(":"); + if (parts.length != 2) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "id", + null, + "must have the format 'groupId:artifactId' but is '" + id + "'.", + exclusion); + } + if (exclusion.getGroupId() != null && !exclusion.getGroupId().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "groupId", + null, + "must not be specified when the 'id' attribute is used.", + exclusion); + } + if (exclusion.getArtifactId() != null && !exclusion.getArtifactId().isEmpty()) { + addViolation( + problems, + Severity.ERROR, + Version.V42, + prefix + "artifactId", + null, + "must not be specified when the 'id' attribute is used.", + exclusion); + } + } + private void validate20RawDependenciesSelfReferencing( ModelProblemCollector problems, Model model, List dependencies, String prefix) { // We only check for groupId/artifactId/version/classifier cause if there is another diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelNormalizerTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelNormalizerTest.java new file mode 100644 index 000000000000..247017ddba09 --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelNormalizerTest.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.impl.model; + +import org.apache.maven.api.model.Dependency; +import org.apache.maven.api.model.Exclusion; +import org.apache.maven.api.model.Mixin; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class DefaultModelNormalizerTest { + + private final DefaultModelNormalizer normalizer = new DefaultModelNormalizer(); + + @Test + void testExpandDependencyId() { + Dependency dep = + Dependency.newBuilder().id("org.slf4j:slf4j-api:2.0.17").build(); + + Dependency result = normalizer.expandDependencyId(dep); + + assertEquals("org.slf4j", result.getGroupId()); + assertEquals("slf4j-api", result.getArtifactId()); + assertEquals("2.0.17", result.getVersion()); + assertEquals("org.slf4j:slf4j-api:2.0.17", result.getId()); + } + + @Test + void testExpandDependencyIdDoesNotOverrideExistingFields() { + Dependency dep = Dependency.newBuilder() + .id("org.slf4j:slf4j-api:2.0.17") + .groupId("org.override") + .build(); + + Dependency result = normalizer.expandDependencyId(dep); + + // Existing groupId should not be overridden + assertEquals("org.override", result.getGroupId()); + assertEquals("slf4j-api", result.getArtifactId()); + assertEquals("2.0.17", result.getVersion()); + } + + @Test + void testExpandDependencyIdInvalidFormat() { + Dependency dep = Dependency.newBuilder().id("invalid-no-colons").build(); + + Dependency result = normalizer.expandDependencyId(dep); + + // Invalid format — fields not populated, validator will catch this + assertNull(result.getGroupId()); + assertNull(result.getArtifactId()); + } + + @Test + void testExpandDependencyIdNull() { + Dependency dep = Dependency.newBuilder() + .groupId("org.example") + .artifactId("my-lib") + .version("1.0") + .build(); + + Dependency result = normalizer.expandDependencyId(dep); + + // No id attribute, should return unchanged + assertEquals("org.example", result.getGroupId()); + assertEquals("my-lib", result.getArtifactId()); + assertEquals("1.0", result.getVersion()); + } + + @Test + void testExpandExclusionId() { + Exclusion exc = Exclusion.newBuilder().id("com.example:unwanted-lib").build(); + + Exclusion result = normalizer.expandExclusionId(exc); + + assertEquals("com.example", result.getGroupId()); + assertEquals("unwanted-lib", result.getArtifactId()); + } + + @Test + void testExpandExclusionIdWildcard() { + Exclusion exc = Exclusion.newBuilder().id("*:*").build(); + + Exclusion result = normalizer.expandExclusionId(exc); + + assertEquals("*", result.getGroupId()); + assertEquals("*", result.getArtifactId()); + } + + @Test + void testExpandExclusionIdNull() { + Exclusion exc = + Exclusion.newBuilder().groupId("org.example").artifactId("lib").build(); + + Exclusion result = normalizer.expandExclusionId(exc); + + // No id attribute, should return same instance + assertEquals("org.example", result.getGroupId()); + assertEquals("lib", result.getArtifactId()); + } + + @Test + void testExpandMixinGav() { + Mixin mixin = + Mixin.newBuilder().gav("com.example.mixins:java-mixin:1.0.0").build(); + + Mixin result = normalizer.expandMixinGav(mixin); + + assertEquals("com.example.mixins", result.getGroupId()); + assertEquals("java-mixin", result.getArtifactId()); + assertEquals("1.0.0", result.getVersion()); + } + + @Test + void testExpandMixinGavNull() { + Mixin mixin = Mixin.newBuilder() + .groupId("com.example") + .artifactId("my-mixin") + .version("2.0") + .build(); + + Mixin result = normalizer.expandMixinGav(mixin); + + // No gav attribute, should return same instance + assertEquals("com.example", result.getGroupId()); + assertEquals("my-mixin", result.getArtifactId()); + assertEquals("2.0", result.getVersion()); + } + + @Test + void testExpandDependencyIdAlsoExpandsExclusions() { + Exclusion exc = Exclusion.newBuilder().id("com.example:unwanted").build(); + Dependency dep = Dependency.newBuilder() + .id("org.slf4j:slf4j-api:2.0.17") + .exclusions(java.util.List.of(exc)) + .build(); + + Dependency result = normalizer.expandDependencyId(dep); + + assertEquals("org.slf4j", result.getGroupId()); + assertEquals("slf4j-api", result.getArtifactId()); + assertEquals("2.0.17", result.getVersion()); + assertEquals(1, result.getExclusions().size()); + assertEquals("com.example", result.getExclusions().get(0).getGroupId()); + assertEquals("unwanted", result.getExclusions().get(0).getArtifactId()); + } +} diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java index df38b262f720..49bcf0237c83 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -1009,4 +1010,107 @@ void profileActivationConditionWithBasedirExpression() throws Exception { "raw-model/profile-activation-condition-with-basedir.xml", ModelValidator.VALIDATION_LEVEL_STRICT); assertViolations(result, 0, 0, 0); } + + // ===== id attribute tests ===== + + @Test + void testValidDependencyIdAttribute() throws Exception { + SimpleProblemCollector result = validateFile("dependency-id-valid.xml"); + assertViolations(result, 0, 0, 0); + } + + @Test + void testDependencyIdBadFormat() throws Exception { + SimpleProblemCollector result = validateFile("dependency-id-bad-format.xml"); + assertFalse( + result.getErrors().isEmpty(), + "Expected errors. Fatals: " + result.getFatals() + " Warnings: " + result.getWarnings()); + assertTrue( + result.getErrors().stream().anyMatch(e -> e.contains("groupId:artifactId:version")), + "Expected error about format. Errors: " + result.getErrors()); + } + + @Test + void testDependencyIdConflictWithMultipleChildElements() throws Exception { + SimpleProblemCollector result = validateFile("dependency-id-conflict-multiple.xml"); + assertEquals(2, result.getErrors().size(), "Expected 2 errors in " + result.getErrors()); + assertTrue( + result.getErrors().stream().anyMatch(e -> e.contains("must not be specified when the 'id' attribute")), + "Expected conflict error. Errors: " + result.getErrors()); + } + + @Test + void testDependencyIdConflictWithGroupIdElements() throws Exception { + SimpleProblemCollector result = validateFile("dependency-id-conflict-groupId.xml"); + assertTrue( + result.getErrors().stream() + .anyMatch(e -> e.contains( + "'dependencies.dependency.groupId' must not be specified when the 'id' attribute")), + "Expected conflict error. Errors: " + result.getErrors()); + } + + @Test + void testDependencyIdConflictWithArtifactIdElement() throws Exception { + SimpleProblemCollector result = validateFile("dependency-id-conflict-artifactId.xml"); + assertTrue( + result.getErrors().stream() + .anyMatch(e -> e.contains( + "'dependencies.dependency.artifactId' must not be specified when the 'id' attribute")), + "Expected conflict error. Errors: " + result.getErrors()); + } + + @Test + void testDependencyIdConflictWithVersionElement() throws Exception { + SimpleProblemCollector result = validateFile("dependency-id-conflict-version.xml"); + assertTrue( + result.getErrors().stream() + .anyMatch(e -> e.contains( + "'dependencies.dependency.version' must not be specified when the 'id' attribute")), + "Expected conflict error. Errors: " + result.getErrors()); + } + + @Test + void testValidExclusionIdAttribute() throws Exception { + SimpleProblemCollector result = validateFile("exclusion-id-valid.xml"); + assertViolations(result, 0, 0, 0); + } + + @Test + void testExclusionIdBadFormat() throws Exception { + SimpleProblemCollector result = validateFile("exclusion-id-bad-format.xml"); + assertTrue( + result.getErrors().stream().anyMatch(e -> e.contains("groupId:artifactId")), + "Expected error about format. Errors: " + result.getErrors()); + } + + @Test + void testExclusionIdConflictWithGroupId() throws Exception { + SimpleProblemCollector result = validateFile("exclusion-id-conflict-groupId.xml"); + assertTrue( + result.getErrors().stream() + .anyMatch( + e -> e.contains( + "'dependencies.dependency.exclusions.exclusion.groupId' must not be specified when the 'id' attribute")), + "Expected error about format. Errors: " + result.getErrors()); + } + + @Test + void testExclusionIdConflictWithArtifactId() throws Exception { + SimpleProblemCollector result = validateFile("exclusion-id-conflict-artifactId.xml"); + assertTrue( + result.getErrors().stream() + .anyMatch( + e -> e.contains( + "'dependencies.dependency.exclusions.exclusion.artifactId' must not be specified when the 'id' attribute")), + "Expected error about format. Errors: " + result.getErrors()); + } + + @Test + void testExclusionIdConflictWithMultiple() throws Exception { + SimpleProblemCollector result = validateFile("exclusion-id-conflict-multiple.xml"); + assertEquals(2, result.getErrors().size(), "Expected 2 errors in " + result.getErrors()); + assertTrue( + result.getErrors().stream().anyMatch(e -> e.contains("must not be specified when the 'id' attribute")), + "Expected error about format. Errors: " + result.getErrors()); + } } diff --git a/impl/maven-impl/src/test/resources/poms/validation/dependency-id-bad-format.xml b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-bad-format.xml new file mode 100644 index 000000000000..e1fc8f8a4f35 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-bad-format.xml @@ -0,0 +1,10 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-artifactId.xml b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-artifactId.xml new file mode 100644 index 000000000000..8820417e71f4 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-artifactId.xml @@ -0,0 +1,12 @@ + + 4.2.0 + aid + gid + 0.1 + + + + slf4j-api + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-groupId.xml b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-groupId.xml new file mode 100644 index 000000000000..3b37652a29e1 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-groupId.xml @@ -0,0 +1,12 @@ + + 4.2.0 + aid + gid + 0.1 + + + + org.slf4j + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-multiple.xml b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-multiple.xml new file mode 100644 index 000000000000..9729ef96404e --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-multiple.xml @@ -0,0 +1,13 @@ + + 4.2.0 + aid + gid + 0.1 + + + + org.slf4j + slf4j-api + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-version.xml b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-version.xml new file mode 100644 index 000000000000..a303683e10b9 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-conflict-version.xml @@ -0,0 +1,12 @@ + + 4.2.0 + aid + gid + 0.1 + + + + 2.0.17 + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/dependency-id-valid.xml b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-valid.xml new file mode 100644 index 000000000000..9618e8a7ff41 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/dependency-id-valid.xml @@ -0,0 +1,13 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + test + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-bad-format.xml b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-bad-format.xml new file mode 100644 index 000000000000..035b826aa7f4 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-bad-format.xml @@ -0,0 +1,14 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + + + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-artifactId.xml b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-artifactId.xml new file mode 100644 index 000000000000..5bfcedaba71d --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-artifactId.xml @@ -0,0 +1,16 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + + * + + + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-groupId.xml b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-groupId.xml new file mode 100644 index 000000000000..1d88746adffe --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-groupId.xml @@ -0,0 +1,16 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + + * + + + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-multiple.xml b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-multiple.xml new file mode 100644 index 000000000000..6c55c1652606 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-conflict-multiple.xml @@ -0,0 +1,17 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + + * + * + + + + + diff --git a/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-valid.xml b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-valid.xml new file mode 100644 index 000000000000..163ccd800900 --- /dev/null +++ b/impl/maven-impl/src/test/resources/poms/validation/exclusion-id-valid.xml @@ -0,0 +1,14 @@ + + 4.2.0 + aid + gid + 0.1 + + + + + + + + + diff --git a/impl/maven-support/src/test/java/org/apache/maven/model/v4/MavenStaxReaderTest.java b/impl/maven-support/src/test/java/org/apache/maven/model/v4/MavenStaxReaderTest.java index 8f5b4c355fb3..4be211767e7d 100644 --- a/impl/maven-support/src/test/java/org/apache/maven/model/v4/MavenStaxReaderTest.java +++ b/impl/maven-support/src/test/java/org/apache/maven/model/v4/MavenStaxReaderTest.java @@ -281,6 +281,74 @@ void testLocationReportingForListElements() throws Exception { assertEquals(5, module3Location.getColumnNumber(), "Module 3 should start at column 5"); } + @Test + void testDependencyIdAttribute() throws Exception { + String xml = "\n" + + " 4.2.0\n" + + " \n" + + " \n" + + " \n" + + ""; + + Model model = fromXml(xml); + assertEquals(1, model.getDependencies().size()); + Dependency dep = model.getDependencies().get(0); + assertEquals("org.slf4j:slf4j-api:2.0.17", dep.getId()); + } + + @Test + void testDependencyIdAttributeWithScope() throws Exception { + String xml = "\n" + + " 4.2.0\n" + + " \n" + + " \n" + + " test\n" + + " \n" + + " \n" + + ""; + + Model model = fromXml(xml); + assertEquals(1, model.getDependencies().size()); + Dependency dep = model.getDependencies().get(0); + assertEquals("org.junit.jupiter:junit-jupiter-api:5.14.1", dep.getId()); + assertEquals("test", dep.getScope()); + } + + @Test + void testExclusionIdAttribute() throws Exception { + String xml = "\n" + + " 4.2.0\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Model model = fromXml(xml); + assertEquals(1, model.getDependencies().size()); + Dependency dep = model.getDependencies().get(0); + assertEquals("org.postgresql:postgresql:42.7.3", dep.getId()); + assertEquals(1, dep.getExclusions().size()); + assertEquals("*:*", dep.getExclusions().get(0).getId()); + } + + @Test + void testMixinIdAttribute() throws Exception { + String xml = "\n" + + " 4.2.0\n" + + " \n" + + " \n" + + " \n" + + ""; + + Model model = fromXml(xml); + assertEquals(1, model.getMixins().size()); + assertEquals("com.example:java-mixin:1.0.0", model.getMixins().get(0).getGav()); + } + private Model fromXml(String xml) throws XMLStreamException { MavenStaxReader reader = new MavenStaxReader(); return reader.read(new StringReader(xml), true, null);